changeset 516:1c2f5ee38039

Allow RADIUS Proxies with the app_radgw extension
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 27 Aug 2010 10:59:51 +0900
parents b9167b4de7dc
children d5383f28b96e
files doc/app_radgw.conf.sample extensions/app_radgw/radius.c extensions/app_radgw/radius.h extensions/app_radgw/rgw.h extensions/app_radgw/rgw_clients.c extensions/app_radgw/rgw_conf.l extensions/app_radgw/rgw_conf.y extensions/app_radgw/rgw_main.c extensions/app_radgw/rgw_msg.c extensions/app_radgw/rgw_worker.c
diffstat 10 files changed, 306 insertions(+), 191 deletions(-) [+]
line wrap: on
line diff
--- a/doc/app_radgw.conf.sample	Thu Aug 26 14:10:03 2010 +0900
+++ b/doc/app_radgw.conf.sample	Fri Aug 27 10:59:51 2010 +0900
@@ -48,15 +48,24 @@
 # RADIUS Clients #
 ##################
 
-# Each RADIUS client must be declared in the form: cli = IP / shared-secret ;
+# Each RADIUS client must be declared in the form: 
+#   nas = IP / shared-secret ;
 # IP can be ipv4 or ipv6
 # port can be additionaly restricted with brackets: IP[port] (ex: 192.168.0.1[1812])
 # shared-secret can be a quoted string, or a list of hexadecimal values.
 # examples:
-# cli = 192.168.100.1 / "secret key" ; # the shared secret buffer is 0x736563726574206b6579 (length 10 bytes)
-# cli = fe00::1 / 73 65 63 72 65 74 20 6b 65 79; # same shared secret as previously
+# nas = 192.168.100.1 / "secret key" ; # the shared secret buffer is 0x736563726574206b6579 (length 10 bytes)
+# nas = fe00::1 / 73 65 63 72 65 74 20 6b 65 79; # same shared secret as previously
 # When a packet is received from an IP not declared here, it is discarded.
 
+# If the RADIUS client is a Proxy that forwards messages from different peers, it must be
+# declared instead as follow:
+#   pxy = IP / shared-secret ;
+# Note that it is not recommended to use this gateway implementation with a proxy currently,
+# since the management of duplicate messages might be insufficient.
+
+# The old notation cli = ... is equivalent to nas = ... and kept for backward compatibility.
+
 
 ####################
 #  Authentication  #
--- a/extensions/app_radgw/radius.c	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/radius.c	Fri Aug 27 10:59:51 2010 +0900
@@ -3,7 +3,8 @@
  *  The content from this file comes directly from the hostap project.
  * It is redistributed under the terms of the BSD license, as allowed
  * by the original copyright reproduced bellow.
- *  In addition to this notice, only the #include directives have been modified.
+ *  In addition to this notice, the following changes have been done:
+ *   - created the radius_msg_dump_attr_val function
  */
 #include "rgw_common.h"
 
@@ -217,7 +218,7 @@
 }
 
 
-static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
+void radius_msg_dump_attr_val(struct radius_attr_hdr *hdr)
 {
 	struct radius_attr_type *attr;
 	int i, len;
@@ -225,9 +226,6 @@
 
 	attr = radius_get_attr_type(hdr->type);
 
-	printf("   Attribute %d (%s) length=%d\n",
-	       hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
-
 	if (attr == NULL)
 		return;
 
@@ -284,6 +282,18 @@
 	}
 }
 
+static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
+{
+	struct radius_attr_type *attr;
+
+	attr = radius_get_attr_type(hdr->type);
+
+	printf("   Attribute %d (%s) length=%d\n",
+	       hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+	
+	radius_msg_dump_attr_val(hdr);
+}
+
 
 void radius_msg_dump(struct radius_msg *msg)
 {
--- a/extensions/app_radgw/radius.h	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/radius.h	Fri Aug 27 10:59:51 2010 +0900
@@ -3,8 +3,11 @@
  *  The content from this file comes directly from the hostap project.
  * It is redistributed under the terms of the BSD license, as allowed
  * by the original copyright reproduced bellow.
- *  The file has not been modified, except for this notice.
+ *  The file has not been modified, except for this notice and
+ * declaration of:
+ *  void radius_msg_dump_attr_val(struct radius_attr_hdr *hdr);
  */
+
 /*********************************************************************************/
 
 /*
@@ -215,6 +218,7 @@
 int radius_msg_initialize(struct radius_msg *msg, size_t init_len);
 void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier);
 void radius_msg_free(struct radius_msg *msg);
+void radius_msg_dump_attr_val(struct radius_attr_hdr *hdr);
 void radius_msg_dump(struct radius_msg *msg);
 int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
 		      size_t secret_len);
--- a/extensions/app_radgw/rgw.h	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/rgw.h	Fri Aug 27 10:59:51 2010 +0900
@@ -57,18 +57,12 @@
 		
 		/* The message has a valid Message-Authenticator attribute */
 		unsigned	valid_mac :1;
-		
-		/* The message has a valid NAS-IP(v6)-Address (1) and/or NAS-Identifier (2) attribute */
-		unsigned	valid_nas_info :2;
 	};
 	
 };
 void rgw_msg_free(struct rgw_radius_msg_meta ** msg);
 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_client * cli, struct msg ** diam);
-int rgw_msg_init(void);
 
 /* Local RADIUS server(s) configuration */
 struct rgw_serv {
@@ -96,14 +90,18 @@
 
 
 /* Clients management */
-int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen );
+enum rgw_cli_type { RGW_CLI_NAS, RGW_CLI_PXY };
+int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth);
+int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type );
 int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len);
+int rgw_clients_gettype(struct rgw_client * cli, enum rgw_cli_type *type);
 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_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam);
 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);
+int rgw_clients_init(void);
 void rgw_clients_fini(void);
 int rgw_client_session_add(struct rgw_client * cli, struct session *sess, char * dest_realm, char * dest_host, application_id_t appid);
 int rgw_client_session_stop(struct rgw_client * cli, struct session * sess, int32_t reason);
--- a/extensions/app_radgw/rgw_clients.c	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/rgw_clients.c	Fri Aug 27 10:59:51 2010 +0900
@@ -39,6 +39,8 @@
 
 #include "rgw.h"
 
+#define REVERSE_DNS_SIZE_MAX	512 /* length of our buffer for reverse DNS */
+
 /* Ordered lists of clients. The order relationship is a memcmp on the address zone. 
    For same addresses, the port is compared.
    The same address cannot be added twice, once with a 0-port and once with another port value.
@@ -66,6 +68,7 @@
 	
 	/* The FQDN, realm, and optional aliases */
 	int			 is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */
+	enum rgw_cli_type 	 type; /* is it a proxy ? */
 	char 			*fqdn;
 	size_t			 fqdn_len;
 	char			*realm;
@@ -90,7 +93,7 @@
 
 
 /* create a new rgw_client. the arguments are moved into the structure (to limit malloc & free calls). */
-static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen )
+static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
 {
 	struct rgw_client *tmp = NULL;
 	char buf[255];
@@ -117,6 +120,8 @@
 	memset(tmp, 0, sizeof(struct rgw_client));
 	fd_list_init(&tmp->chain, NULL);
 	
+	tmp->type = type;
+	
 	if (loc) {
 		tmp->is_local = 1;
 	} else {
@@ -230,6 +235,14 @@
 	return 0;
 }
 
+int rgw_clients_gettype(struct rgw_client * cli, enum rgw_cli_type *type)
+{
+	CHECK_PARAMS( cli && type );
+	*type = cli->type;
+	return 0;
+}
+
+
 int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref)
 {
 	int ret = 0;
@@ -303,15 +316,71 @@
 
 /* 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)
+/* NOTE: This function does nothing if the client is a RADIUS Proxy... */
+/* Check if the message has a valid authenticator, and update the meta-data accordingly */
+int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth)
+{
+	unsigned char * key;
+	size_t keylen;
+	int count;
+	
+	TRACE_ENTRY("%p %p %p", msg, cli, req_auth);
+	
+	CHECK_PARAMS(msg && cli);
+	
+	CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen));
+	
+	count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0);
+	if (count > 1) {
+		TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count);
+		return EINVAL;
+	}
+	if (count == 0) {
+		TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes.");
+		msg->valid_mac = 0;
+	} else {
+		if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) {
+			TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message.");
+			return EINVAL;
+		}
+		msg->valid_mac = 1;
+	}
+	
+	return 0;
+}
+
+static struct dict_object * cache_orig_host = NULL;
+static struct dict_object * cache_orig_realm = NULL;
+static struct dict_object * cache_route_record = NULL;
+
+int rgw_clients_init(void)
+{
+	TRACE_ENTRY();
+	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) );
+	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &cache_route_record, ENOENT) );
+	return 0;
+}
+
+
+/* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter
+ message with the appropriate routing information */
+int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam)
 {
 	int idx;
+	int valid_nas_info = 0;
 	struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL;
+	char * oh_str = NULL;
+	char * or_str = NULL;
+	char * rr_str = NULL;
+	char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */
 	
-	TRACE_ENTRY("%p %p", msg, cli);
-	CHECK_PARAMS(msg && cli && !msg->valid_nas_info );
-		
+	struct avp *avp = NULL;
+	union avp_value avp_val;
+	
+	TRACE_ENTRY("%p %p %p", msg, cli, diam);
+	CHECK_PARAMS(msg && cli && diam && (*diam == NULL));
+	
 	/* Find the relevant attributes, if any */
 	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]);
@@ -335,57 +404,111 @@
 		
 	if (!nas_ip && !nas_ip6 && !nas_id) {
 		TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute.");
-		goto end;
+		
+		/* Get information on this peer */
+		CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+		
+		goto diameter;
 	}
 	
 	/* Check if the message was received from the IP in NAS-IP-Address attribute */
 	if (nas_ip && (cli->sa->sa_family == AF_INET) && !memcmp(nas_ip+1, &cli->sin->sin_addr, sizeof(struct in_addr))) {
 		TRACE_DEBUG(FULL, "NAS-IP-Address contains the same address as the message was received from.");
-		msg->valid_nas_info |= 1;
+		valid_nas_info |= 1;
 	}
 	if (nas_ip6 && (cli->sa->sa_family == AF_INET6) && !memcmp(nas_ip6+1, &cli->sin6->sin6_addr, sizeof(struct in6_addr))) {
 		TRACE_DEBUG(FULL, "NAS-IPv6-Address contains the same address as the message was received from.");
-		msg->valid_nas_info |= 1;
+		valid_nas_info |= 2;
 	}
 	
-	/* If these conditions are not met, the message is probably forged (well, this might be false...) */
-	if ((! msg->valid_nas_info) && (nas_ip || nas_ip6)) {
-		/*
-				In RADIUS it would be possible for a rogue NAS to forge the NAS-IP-
-				Address attribute value.  Diameter/RADIUS translation agents MUST
-				check a received NAS-IP-Address or NAS-IPv6-Address attribute against
-				the source address of the RADIUS packet.  If they do not match and
-				the Diameter/RADIUS translation agent does not know whether the
-				packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State
-				attribute), then by default it is assumed that the source address
-				corresponds to a RADIUS proxy, and that the NAS Address is behind
-				that proxy, potentially with some additional RADIUS proxies in
-				between.  The Diameter/RADIUS translation agent MUST insert entries
-				in the Route-Record AVP corresponding to the apparent route.  This
-				implies doing a reverse lookup on the source address and NAS-IP-
-				Address or NAS-IPv6-Address attributes to determine the corresponding
-				FQDNs.
+	
+	/*
+			In RADIUS it would be possible for a rogue NAS to forge the NAS-IP-
+			Address attribute value.  Diameter/RADIUS translation agents MUST
+			check a received NAS-IP-Address or NAS-IPv6-Address attribute against
+			the source address of the RADIUS packet.  If they do not match and
+			the Diameter/RADIUS translation agent does not know whether the
+			packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State
+			attribute), then by default it is assumed that the source address
+			corresponds to a RADIUS proxy, and that the NAS Address is behind
+			that proxy, potentially with some additional RADIUS proxies in
+			between.  The Diameter/RADIUS translation agent MUST insert entries
+			in the Route-Record AVP corresponding to the apparent route.  This
+			implies doing a reverse lookup on the source address and NAS-IP-
+			Address or NAS-IPv6-Address attributes to determine the corresponding
+			FQDNs.
+
+			If the source address and the NAS-IP-Address or NAS-IPv6-Address do
+			not match, and the Diameter/RADIUS translation agent knows that it is
+			talking directly to the NAS (e.g., there are no RADIUS proxies
+			between it and the NAS), then the error should be logged, and the
+			packet MUST be discarded.
 
-				If the source address and the NAS-IP-Address or NAS-IPv6-Address do
-				not match, and the Diameter/RADIUS translation agent knows that it is
-				talking directly to the NAS (e.g., there are no RADIUS proxies
-				between it and the NAS), then the error should be logged, and the
-				packet MUST be discarded.
-
-				Diameter agents and servers MUST check whether the NAS-IP-Address AVP
-				corresponds to an entry in the Route-Record AVP.  This is done by
-				doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve
-				the corresponding FQDN, and by checking for a match with the Route-
-				Record AVP.  If no match is found, then an error is logged, but no
-				other action is taken.
-		*/
-		TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different from the sender's. Discarding...");
-		return ENOTSUP;
+			Diameter agents and servers MUST check whether the NAS-IP-Address AVP
+			corresponds to an entry in the Route-Record AVP.  This is done by
+			doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve
+			the corresponding FQDN, and by checking for a match with the Route-
+			Record AVP.  If no match is found, then an error is logged, but no
+			other action is taken.
+	*/
+	if (nas_ip || nas_ip6) {
+		if (!valid_nas_info) {
+			if (cli->type == RGW_CLI_NAS) {
+				TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different \nfrom the sender's. Please configure as Proxy if this is expected.\n Message discarded.");
+				return EINVAL;
+			} else {
+				/* the peer is configured as a proxy, so accept the message */
+				sSS ss;
+				
+				/* In that case, the cli will be stored as Route-Record and the NAS-IP-Address as origin */
+				if (!cli->is_local) {
+					rr_str = cli->fqdn;
+				}
+				
+				/* We must DNS-reverse the NAS-IP*-Address */
+				memset(&ss, 0 , sizeof(sSS));
+				if (nas_ip) {
+					sSA4 * sin = (sSA4 *)&ss;
+					sin->sin_family = AF_INET;
+					memcpy(&sin->sin_addr, nas_ip + 1, sizeof(struct in_addr));
+				} else {
+					sSA6 * sin6 = (sSA6 *)&ss;
+					sin6->sin6_family = AF_INET6;
+					memcpy(&sin6->sin6_addr, nas_ip6 + 1, sizeof(struct in6_addr));
+				}
+				CHECK_SYS_DO( getnameinfo( (sSA *)&ss, sSAlen(&ss), &buf[0], sizeof(buf), NULL, 0, NI_NAMEREQD),
+					{
+						TRACE_DEBUG(INFO, "The NAS-IP*-Address cannot be DNS reversed in order to create the Origin-Host AVP; rejecting the message (translation is impossible).");
+						return EINVAL;
+					} );
+				
+				oh_str = &buf[0];
+				or_str = strchr(oh_str, '.');
+				if (or_str) {
+					or_str ++; /* move after the first dot */
+					if (*or_str == '\0')
+						or_str = NULL; /* Discard this realm, we will use the local realm later */
+				}
+			}
+		} else {
+			/* The attribute matches the source address, just use this in origin-host */
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+		}
+		
+		goto diameter; /* we ignore the nas_id in that case */
 	}
 	
-	/* Now check the nas_id, but only for non-local hosts */
-	if (nas_id && (! cli->is_local) ) {
-		char * str;
+	/* We don't have a NAS-IP*-Address attribute if we are here */
+	if (cli->is_local) {
+		/* Simple: we use our own configuration */
+		CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+		goto diameter;
+	}
+	
+	/* At this point, we only have nas_id, and the client is not local */
+	ASSERT(nas_id);
+	
+	{
 		int found, ret;
 		struct addrinfo hint, *res, *ptr;
 		
@@ -409,7 +532,7 @@
 		/* first, check if the nas_id is the fqdn of the peer or a known alias */
 		if ((cli->fqdn_len == (nas_id->length - sizeof(struct radius_attr_hdr))) 
 		&& (!strncasecmp((char *)(nas_id + 1), cli->fqdn, nas_id->length - sizeof(struct radius_attr_hdr)))) {
-			TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the NAS");
+			TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the client");
 			found = 1;
 		} else {
 			for (idx = 0; idx < cli->aliases_nb; idx++) {
@@ -423,52 +546,106 @@
 		}
 		
 		if (found) {
-			msg->valid_nas_info |= 2;
-			goto end;
+			/* The NAS-Identifier matches the source IP */
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+
+			goto diameter;
 		}
 		
-		/* copy the identifier, we try to DNS resolve it */
-		CHECK_MALLOC( str = malloc(nas_id->length - sizeof(struct radius_attr_hdr) + 1) );
-		memcpy(str, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr));
-		str[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0';
+		/* Attempt DNS resolution of the identifier */
+		ASSERT( nas_id->length - sizeof(struct radius_attr_hdr) < sizeof(buf) );
+		memcpy(buf, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr));
+		buf[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0';
 		
 		/* Now check if this alias is valid for this peer */
 		memset(&hint, 0, sizeof(hint));
-		hint.ai_family = cli->sa->sa_family;
 		hint.ai_flags  = AI_CANONNAME;
-		ret = getaddrinfo(str, NULL, &hint, &res);
+		ret = getaddrinfo(buf, NULL, &hint, &res);
 		if (ret == 0) {
-			/* The name was resolved correctly, it must match the IP of the client: */
+			strncpy(buf, res->ai_canonname, sizeof(buf));
+			/* The name was resolved correctly, does it match the IP of the client? */
 			for (ptr = res; ptr != NULL; ptr = ptr->ai_next) {
 				if (cli->sa->sa_family != ptr->ai_family)
 					continue;
 				if (memcmp(cli->sa, ptr->ai_addr, sSAlen(cli->sa)))
 					continue;
 				
-				/* It matches: the alias is valid */
 				found = 1;
 				break;
 			}
 			freeaddrinfo(res);
 			
 			if (!found) {
-				TRACE_DEBUG(INFO, "The NAS-Identifier value '%s' resolves to a different IP from the NAS's, discarding the message.", str);
-				free(str);
-				return EINVAL;
+				if (cli->type == RGW_CLI_NAS) {
+					TRACE_DEBUG(INFO, "The NAS-Identifier value '%.*s' resolves to a different IP than the client's, discarding the message. \nConfigure this client as a Proxy if this message should be valid.", 
+						nas_id->length - sizeof(struct radius_attr_hdr), nas_id + 1);
+					return EINVAL;
+				} else {
+					/* This identifier matches a different IP, assume it is a proxied message */
+					if (!cli->is_local) {
+						rr_str = cli->fqdn;
+					}
+					oh_str = &buf[0]; /* The canonname resolved */
+					or_str = strchr(oh_str, '.');
+					if (or_str) {
+						or_str ++; /* move after the first dot */
+						if (*or_str == '\0')
+							or_str = NULL; /* Discard this realm, we will use the local realm later */
+					}
+				}
+			} else {
+				/* It is a valid alias, save it */
+				CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) );
+				CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1] = malloc( 1 + nas_id->length - sizeof(struct radius_attr_hdr) ));
+				memcpy( cli->aliases[cli->aliases_nb + 1], nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr));
+				*(cli->aliases[cli->aliases_nb + 1] + nas_id->length - sizeof(struct radius_attr_hdr)) = '\0';
+				cli->aliases_nb ++;
+				TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", cli->aliases[cli->aliases_nb + 1], cli->fqdn);
+				CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
 			}
 		} else {
 			/* Error resolving the name */
-			TRACE_DEBUG(INFO, "Error while resolving NAS-Identifier value '%s': %s. Ignoring...", str, gai_strerror(ret));
+			TRACE_DEBUG(INFO, "NAS-Identifier '%s' cannot be resolved: %s. Ignoring...", buf, gai_strerror(ret));
+			/* Assume this is a valid identifier for the client */
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
 		}
-		
-		/* It is a valid alias, save it */
-		CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) );
-		cli->aliases[cli->aliases_nb + 1] = str;
-		cli->aliases_nb ++;
-		TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", str, cli->fqdn);
-		msg->valid_nas_info |= 2;
 	}
-end:	
+	
+	/* Now, let's create the empty Diameter message with Origin-Host, -Realm, and Route-Record if needed. */
+diameter:
+	ASSERT(oh_str); /* If it is not defined here, there is a bug... */
+	if (!or_str)
+		or_str = fd_g_config->cnf_diamrlm; /* Use local realm in that case */
+	
+	/* Create an empty Diameter message so that extensions can store their AVPs */
+	CHECK_FCT(  fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam )  );
+	
+	/* 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));
+	avp_val.os.data = (unsigned char *)oh_str;
+	avp_val.os.len = strlen(oh_str);
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
+	
+	/* Add the Origin-Realm as next AVP */
+	CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) );
+	memset(&avp_val, 0, sizeof(avp_val));
+	avp_val.os.data = (unsigned char *)or_str;
+	avp_val.os.len = strlen(or_str);
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
+	
+	if (rr_str) {
+		CHECK_FCT( fd_msg_avp_new ( cache_route_record, 0, &avp ) );
+		memset(&avp_val, 0, sizeof(avp_val));
+		avp_val.os.data = (unsigned char *)rr_str;
+		avp_val.os.len = strlen(rr_str);
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	/* Done! */
 	return 0;
 }
 
@@ -507,7 +684,7 @@
 	CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), );
 }
 
-int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen )
+int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
 {
 	struct rgw_client * prev = NULL, *new = NULL;
 	int ret;
@@ -516,10 +693,11 @@
 	
 	CHECK_PARAMS( ip_port && key && *key && keylen );
 	CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) );
+	CHECK_PARAMS( (type == RGW_CLI_NAS) || (type == RGW_CLI_PXY) );
 	
 	/* Dump the entry in debug mode */
 	if (TRACE_BOOL(FULL + 1 )) {
-		TRACE_DEBUG(FULL, "Adding client:");
+		TRACE_DEBUG(FULL, "Adding %s:", (type == RGW_CLI_NAS) ? "NAS" : "PROXY"  );
 		TRACE_DEBUG_sSA(FULL, 	 "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" );
 		TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]" );
 	}
@@ -531,7 +709,7 @@
 	ret = client_search(&prev, ip_port );
 	if (ret == ENOENT) {
 		/* No duplicate found, Ok to add */
-		CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen ), goto end );
+		CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen, type ), goto end );
 		fd_list_insert_after(&prev->chain, &new->chain);
 		new->refcount++;
 		ret = 0;
@@ -540,17 +718,17 @@
 	
 	if (ret == EEXIST) {
 		/* Check if the key is the same, then skip or return an error */
-		if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) )) {
+		if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) ) && (type == prev->type)) {
 			TRACE_DEBUG(INFO, "Skipping duplicate client description");
 			ret = 0;
 			goto end;
 		}
 		
 		fd_log_debug("ERROR: Conflicting RADIUS clients descriptions!\n");
-		TRACE_DEBUG(NONE, "Previous entry:");
+		TRACE_DEBUG(NONE, "Previous entry: %s", (prev->type == RGW_CLI_NAS) ? "NAS" : "PROXY");
 		TRACE_DEBUG_sSA(NONE, 	 "\tIP : ", prev->sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
 		TRACE_DEBUG_BUFFER(NONE, "\tKey: [", prev->key.data, prev->key.len, "]" );
-		TRACE_DEBUG(NONE, "Conflicting entry:");
+		TRACE_DEBUG(NONE, "Conflicting entry: %s", (type == RGW_CLI_NAS) ? "NAS" : "PROXY");
 		TRACE_DEBUG_sSA(NONE, 	 "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" );
 		TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]" );
 	}
--- a/extensions/app_radgw/rgw_conf.l	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/rgw_conf.l	Fri Aug 27 10:59:51 2010 +0900
@@ -121,7 +121,8 @@
 
 
 	/* Client section */
-(?i:"cli")		{ BEGIN(IN_CLI1); return CLI_PREFIX; 		}
+(?i:"nas"|"cli")	{ BEGIN(IN_CLI1); yylval->integer=RGW_CLI_NAS; return NAS_OR_PXY; 		}
+(?i:"pxy")		{ BEGIN(IN_CLI1); yylval->integer=RGW_CLI_PXY; return NAS_OR_PXY; 		}
 
 	/* Match an IP (4 or 6) and optional port */
 <IN_CLI1>({IP4}|{IP6}){BR_PORT}?	{
--- a/extensions/app_radgw/rgw_conf.y	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/rgw_conf.y	Fri Aug 27 10:59:51 2010 +0900
@@ -145,13 +145,14 @@
 
 %type <string>	FINDFILEEXT
 
+%token <integer> NAS_OR_PXY
+
 /* simple tokens */
 %token		DISABLED
 %token		AUTH
 %token		ACCT
 
 %token 		PLG_PREFIX
-%token 		CLI_PREFIX
 
 %token		AUTH_ENABLE
 %token		AUTH_PORT
@@ -263,10 +264,10 @@
 clientdef:		{
 				buf_reinit();
 			}
-			CLI_PREFIX '=' IP '/' clisecret_key ';'
+			NAS_OR_PXY '=' IP '/' clisecret_key ';'
 			{
 				/* Add this client */
-				if ( rgw_clients_add( $4, &buf, buf_sz ) ) {
+				if ( rgw_clients_add( $4, &buf, buf_sz, $2 ) ) {
 					yyerror (&yylloc, conffile, "Error parsing / adding client !");
 					YYERROR;
 				}
--- a/extensions/app_radgw/rgw_main.c	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/rgw_main.c	Fri Aug 27 10:59:51 2010 +0900
@@ -41,7 +41,7 @@
 /* Extension entry point called by freeDiameter */
 static int rgw_main(char * conffile) 
 {
-	CHECK_FCT( rgw_msg_init() );
+	CHECK_FCT( rgw_clients_init() );
 	
 	CHECK_FCT( rgw_servers_init() );
 	
--- a/extensions/app_radgw/rgw_msg.c	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/rgw_msg.c	Fri Aug 27 10:59:51 2010 +0900
@@ -78,38 +78,6 @@
 	return 0;
 }
 
-/* Check if the message has a valid authenticator, and update the meta-data accordingly */
-int rgw_msg_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth)
-{
-	unsigned char * key;
-	size_t keylen;
-	int count;
-	
-	TRACE_ENTRY("%p %p %p", msg, cli, req_auth);
-	
-	CHECK_PARAMS(msg && cli);
-	
-	CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen));
-	
-	count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0);
-	if (count > 1) {
-		TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count);
-		return EINVAL;
-	}
-	if (count == 0) {
-		TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes.");
-		msg->valid_mac = 0;
-	} else {
-		if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) {
-			TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message.");
-			return EINVAL;
-		}
-		msg->valid_mac = 1;
-	}
-	
-	return 0;
-}
-
 /* Dump a message (inspired from radius_msg_dump) -- can be used safely with a struct radius_msg as parameter (we don't dump the metadata) */
 void rgw_msg_dump(struct rgw_radius_msg_meta * msg)
 {
@@ -131,56 +99,8 @@
 	for (i = 0; i < msg->radius.attr_used; i++) {
 		struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[i]);
 		fd_log_debug("    - len:%3hhu, type:0x%02hhx (%s)\n", attr->length, attr->type, rgw_msg_attrtype_str(attr->type));
-		/* If we need to dump the value, it's better to call directly radius_msg_dump instead... */
+		radius_msg_dump_attr_val(attr);
 	}
 	fd_log_debug("-----------------------------\n");
 }
 
-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, "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 new Diameter msg with origin-host & realm */
-int rgw_msg_create_base(struct rgw_client * cli, struct msg ** diam)
-{
-	char * fqdn;
-	char * realm;
-	
-	struct avp *avp = NULL;
-	union avp_value avp_val;
-	
-	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 an empty Diameter message so that extensions can store their AVPs */
-	CHECK_FCT(  fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam )  );
-	
-	/* 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));
-	avp_val.os.data = (unsigned char *)fqdn;
-	avp_val.os.len = strlen(fqdn);
-	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
-	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
-	
-	/* Add the Origin-Realm as next AVP */
-	CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) );
-	memset(&avp_val, 0, sizeof(avp_val));
-	avp_val.os.data = (unsigned char *)realm;
-	avp_val.os.len = strlen(realm);
-	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
-	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
-	
-	/* Done! */
-	return 0;
-}
--- a/extensions/app_radgw/rgw_worker.c	Thu Aug 26 14:10:03 2010 +0900
+++ b/extensions/app_radgw/rgw_worker.c	Fri Aug 27 10:59:51 2010 +0900
@@ -96,7 +96,7 @@
 		/* process the data */
 		
 		/* Check authenticator, if any */
-		CHECK_FCT_DO( rgw_msg_auth_check(msg, cli, NULL),
+		CHECK_FCT_DO( rgw_clients_auth_check(msg, cli, NULL),
 			{
 				/* An error occurred, discard message */
 				rgw_msg_free(&msg);
@@ -117,22 +117,16 @@
 			continue; /* the message was a duplicate */
 		}
 		
-		/* Check that IP is coherent with the identity in the message */
-		CHECK_FCT_DO( rgw_clients_check_origin(msg, cli),
+		diam_msg = NULL;
+		/* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore. */
+	
+		/* Check that IP is coherent with the identity in the message, and create an empty message with only Origin information */
+		CHECK_FCT_DO( rgw_clients_create_origin(msg, cli, &diam_msg),
 			{
 				/* An error occurred, discard message */
-				rgw_msg_free(&msg);
-				rgw_clients_dispose(&cli);
-				continue;
-			}  );
-		
-		/* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore. */
-		diam_msg = NULL;
-		
-		/* 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 */
+				if (diam_msg) {
+					CHECK_FCT_DO( fd_msg_free(diam_msg), );
+				}
 				rgw_msg_free(&msg);
 				rgw_clients_dispose(&cli);
 				continue;
"Welcome to our mercurial repository"