Index: doc/app_radgw.conf.sample
===================================================================
--- doc/app_radgw.conf.sample	(revision 254)
+++ doc/app_radgw.conf.sample	(revision 254)
@@ -0,0 +1,89 @@
+# This file contains information for configuring the app_radgw extension.
+# To find how to have freeDiameter load this extension, please refer to the freeDiameter documentation.
+#
+# The app_radgw extension allows a freeDiameter agent to serve as a
+# RADIUS/Diameter gateway. Typically, a RADIUS client (e.g. a NAS) will connect to
+# this agent, and the message will be converted to Diameter and sent to a Diameter server.
+#
+# Note that this extension does not provide a fully functionnal RADIUS/Diameter gateway.
+# You need to load plugins to handle specific RADIUS messages and convert them to 
+# Diameter apps such as NASREQ, EAP, ... See the next section for information.
+
+
+###########
+# PLUGINS #
+###########
+
+# Additional plugins must be loaded to support specific RADIUS messages and attributes.
+
+# Plugins are registered either for every message, or by port (auth or acct), or by port and code.
+# The general format is:
+# RGWX = plugin [: conf_file] [: port] [: code(s)] ;
+#  Where:
+#    plugin is the quoted file name (relative or absolute) of the plugin to load (.rgwx files).
+#    conf_file (optional) is the quoted name of the configuration file.
+#    port (optional), either auth or acct. 
+#       If not specified, extension is called for messages incoming on both ports
+#    code(s): space-separated list of command codes for which this extension must be called.
+#       If not specified, the extension is called for all incoming messages.
+#       The values are interpreted as hexadecimal.
+#
+# The plugins are called in the order they appear in this file.
+# Here are some explained examples:
+#  RGWX = "3579.rgwx";   Load this extension and call it for all messages. No configuration file.
+#  RGWX = "3579.rgwx" : "3579.conf";  Same as previous but with a configuration file specified.
+#  RGWX = "3579.rgwx" : auth; No configuration file, but called only for RADIUS messages received on authentication port.
+#  RGWX = "3579.rgwx" : 4 8 b;  Called for messages with command code 4, 8, or 11 only.
+#  RGWX = "3579.rgwx" : "3579.conf" : auth : 4 8 b;  All parameters combined.
+
+# Once the list of extensions for an incoming message has been called (or if the list is empty), 
+# an error is logged if some RADIUS attributes of the message have not been handled.
+
+
+##################
+# RADIUS Clients #
+##################
+
+# Each RADIUS client must be declared in the form: cli = 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
+# When a packet is received from an IP not declared here, it is discarded.
+
+
+####################
+#  Authentication  #
+#  Authorization   #
+####################
+
+# Enable the RADIUS/Diameter authentication/authorization gateway?
+# auth_server_enable = 1;
+
+# The port on which the accounting server listens
+# auth_server_port = 1812;
+
+# The IPv4 on which to bind the server, or "disable" if IPv4 must not be used.
+# auth_server_ip4 = 0.0.0.0;
+
+# The IPv6 address to which the server is bound, or "disable"
+# auth_server_ip6 = :: ;
+
+
+################
+#  Accounting  #
+################
+
+# Enable the RADIUS/Diameter accounting gateway?
+# acct_server_enable = 1;
+
+# The port on which the accounting server listens
+# acct_server_port = 1813;
+
+# The IPv4 on which to bind the server, or "disable" if no IPv4 is wanted.
+# acct_server_ip4 = 0.0.0.0;
+
+# The IPv6 address to which the server is bound, or "disable"
+# acct_server_ip6 = :: ;
Index: extensions/CMakeLists.txt
===================================================================
--- extensions/CMakeLists.txt	(revision 238)
+++ extensions/CMakeLists.txt	(revision 254)
@@ -27,4 +27,5 @@
 ####
 # Diameter applications dictionary
+
 OPTION(BUILD_DICT_NASREQ "Build NASREQ (RFC4005) Dictionary definitions?" ON)
 	IF (BUILD_DICT_NASREQ)
@@ -40,9 +41,11 @@
 
 ####
-# Radius / Diameter gateway extension(s)
-# OPTION(BUILD_RADIUS_GW "Build radius_gw? (one-way RADIUS/Diameter gateway - RADIUS NAS <-> Diameter server)" ON)
-# 	IF (BUILD_RADIUS_GW)
-# 	   SUBDIRS(radius_gw)
-# 	ENDIF (BUILD_RADIUS_GW)
+# Diameter applications
+
+OPTION(BUILD_APP_RADGW "Build app_radgw? (one-way RADIUS/Diameter gateway - RADIUS NAS <-> Diameter server)" OFF)
+	IF (BUILD_APP_RADGW)
+	   SUBDIRS(app_radgw)
+	ENDIF (BUILD_APP_RADGW)
+
 
 
@@ -65,6 +68,7 @@
 
 
+
 ####
-# Debug / test extensions
+# Debug & test extensions
 
 OPTION(BUILD_SAMPLE "Build sample.fdx? (Simple extension to demonstrate extension mechanism, for developpers only)" OFF)
Index: extensions/app_radgw/CMakeLists.txt
===================================================================
--- extensions/app_radgw/CMakeLists.txt	(revision 254)
+++ extensions/app_radgw/CMakeLists.txt	(revision 254)
@@ -0,0 +1,106 @@
+# The app_radgw extension
+PROJECT("RADIUS/Diameter extensible gateway application for freeDiameter" C)
+
+
+# Overwrite the debug level for the extension code if configured
+OPTION(DEBUG_LEVEL_APP_RADGW "Overwrite debug level for the extension app_radgw if defined" OFF)
+IF (DEBUG_LEVEL_APP_RADGW)
+     ADD_DEFINITIONS(-DTRACE_LEVEL=${DEBUG_LEVEL_APP_RADGW})
+ENDIF (DEBUG_LEVEL_APP_RADGW)
+
+
+########### Utility library #############
+# utility libray for the extension and its plugins
+# See rgw_common.h for detail
+
+SET( RG_COMMON_SRC
+	radius.c
+	md5.c
+)
+SET( RG_COMMON_HEADER
+	rgw_common.h
+	radius.h
+	md5.h
+	hostap_compat.h
+)
+ADD_LIBRARY(rgw_common ${RG_COMMON_SRC})
+
+
+
+########### Main app_radgw extension #############
+
+# Parser files
+BISON_FILE(rgw_conf.y)
+FLEX_FILE(rgw_conf.l)
+SET_SOURCE_FILES_PROPERTIES(lex.rgw_conf.c rgw_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+
+# List of source files
+SET( RGW_DEFAULT_SRC
+	rgw_main.c
+	lex.rgw_conf.c
+	rgw_conf.tab.c
+	rgw_conf.tab.h
+	rgw_clients.c
+	rgw_plugins.c
+	rgw_msg.c
+	rgw_servers.c
+	rgw_worker.c
+)
+
+# Compile these files as a freeDiameter extension.
+FD_ADD_EXTENSION(app_radgw ${RGW_DEFAULT_SRC} ${RG_COMMON_HEADER})
+TARGET_LINK_LIBRARIES(app_radgw rgw_common)
+
+
+
+
+
+
+
+
+
+
+
+
+########### Sub extensions #############
+# Example of support extension:
+# OPTION(BUILD_RADIUS_GW_SAMPLE "Build sample sub-extension? (for debug only)" OFF)
+#  	IF (BUILD_RADIUS_GW_SAMPLE)
+#  	   ADD_LIBRARY(sub_sample MODULE ${RG_COMMON_HEADER} sub_sample.c)
+# 	   TARGET_LINK_LIBRARIES(sub_sample rg_common)
+#  	ENDIF (BUILD_RADIUS_GW_SAMPLE)
+	
+# OPTION(BUILD_SUB_DEBUG "Build debug sub-extension? (display status of RADIUS and Diameter messages)" ON)
+#  	IF (BUILD_SUB_DEBUG)
+#  	   ADD_LIBRARY(sub_debug MODULE ${RG_COMMON_HEADER} sub_debug.c)
+# 	   TARGET_LINK_LIBRARIES(sub_debug rg_common)
+#  	ENDIF (BUILD_SUB_DEBUG)
+# 
+# OPTION(BUILD_SUB_ECHO_DROP "Build 'echo/drop' sub-extension? (echo or drop specific RADIUS attributes, no Diameter translation)" ON)
+#  	IF (BUILD_SUB_ECHO_DROP)
+# 	   ADD_DEFINITIONS(-DSUB_ECHO_DROP_VERBO=2)
+#  	   BISON_FILE(sub_echo_drop.y)
+#  	   FLEX_FILE(sub_echo_drop.l)
+#  	   SET_SOURCE_FILES_PROPERTIES(lex.sub_echo_drop.c sub_echo_drop.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+#  	   ADD_LIBRARY(sub_echo_drop MODULE ${RG_COMMON_HEADER}
+# 	   	sub_echo_drop.c
+# 		sub_echo_drop.h
+# 		lex.sub_echo_drop.c
+# 		sub_echo_drop.tab.c
+# 		sub_echo_drop.tab.h)
+# 	   TARGET_LINK_LIBRARIES(sub_echo_drop rg_common)
+#  	ENDIF (BUILD_SUB_ECHO_DROP)
+# 
+# OPTION(BUILD_SUB_AUTH "Build RADIUS Authentication & Authorization sub-extension? (RFC2865, RFC3579)" ON)
+#  	IF (BUILD_SUB_AUTH)
+# 	   ADD_DEFINITIONS(-DSUB_AUTH_VERBO=2)
+#  	   ADD_LIBRARY(sub_auth MODULE ${RG_COMMON_HEADER} sub_auth.c)
+# 	   TARGET_LINK_LIBRARIES(sub_auth rg_common)
+#  	ENDIF (BUILD_SUB_AUTH)
+# 
+# OPTION(BUILD_SUB_ACCT "Build RADIUS Accounting sub-extension? (RFC2866)" ON)
+#  	IF (BUILD_SUB_ACCT)
+# 	   ADD_DEFINITIONS(-DSUB_ACCT_VERBO=2)
+#  	   ADD_LIBRARY(sub_acct MODULE ${RG_COMMON_HEADER} sub_acct.c)
+# 	   TARGET_LINK_LIBRARIES(sub_acct rg_common)
+#  	ENDIF (BUILD_SUB_ACCT)
Index: extensions/app_radgw/hostap_compat.h
===================================================================
--- extensions/app_radgw/hostap_compat.h	(revision 254)
+++ extensions/app_radgw/hostap_compat.h	(revision 254)
@@ -0,0 +1,194 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* This file contains compatibility bindings for hostap files.
+ Most of the definitions here come from different files from the hostap project.
+ 
+ We don't care for OS-specific definitions since we are only compatible with POSIX systems.
+ 
+ */
+
+#ifndef _HOSTAP_COMPAT_H
+#define _HOSTAP_COMPAT_H
+
+#include <sys/time.h>
+
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+
+/* md5.c uses a different macro name than freeDiameter for endianness */
+#ifdef HOST_BIG_ENDIAN
+# define WORDS_BIGENDIAN HOST_BIG_ENDIAN
+#else /* HOST_BIG_ENDIAN */
+# undef WORDS_BIGENDIAN
+#endif /* HOST_BIG_ENDIAN */
+
+/* freeDiameter uses the POSIX API, so we don't provide alternatives. This may be changed later as needed */
+#define os_malloc(s) malloc((s))
+#define os_realloc(p, s) realloc((p), (s))
+#define os_free(p) free((p))
+
+#define os_memcpy(d, s, n) memcpy((d), (s), (n))
+#define os_memmove(d, s, n) memmove((d), (s), (n))
+#define os_memset(s, c, n) memset(s, c, n)
+#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
+
+#define os_strdup(s) strdup(s)
+#define os_strlen(s) strlen(s)
+#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
+#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
+#define os_strchr(s, c) strchr((s), (c))
+#define os_strcmp(s1, s2) strcmp((s1), (s2))
+#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
+#define os_strncpy(d, s, n) strncpy((d), (s), (n))
+#define os_strrchr(s, c) strrchr((s), (c))
+#define os_strstr(h, n) strstr((h), (n))
+#define os_snprintf snprintf
+
+#define os_random() random()
+
+static __inline__ void * os_zalloc(size_t size)
+{
+	void *n = os_malloc(size);
+	if (n)
+		os_memset(n, 0, size);
+	return n;
+}
+
+typedef long os_time_t;
+struct os_time {
+	os_time_t sec;
+	os_time_t usec;
+};
+
+static __inline__ int os_get_time(struct os_time *t)
+{
+	int res;
+	struct timeval tv;
+	res = gettimeofday(&tv, NULL);
+	t->sec = tv.tv_sec;
+	t->usec = tv.tv_usec;
+	return res;
+}
+
+/* Macros for handling unaligned memory accesses */
+#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1]))
+#define WPA_PUT_BE16(a, val)			\
+	do {					\
+		(a)[0] = ((u16) (val)) >> 8;	\
+		(a)[1] = ((u16) (val)) & 0xff;	\
+	} while (0)
+
+#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
+#define WPA_PUT_LE16(a, val)			\
+	do {					\
+		(a)[1] = ((u16) (val)) >> 8;	\
+		(a)[0] = ((u16) (val)) & 0xff;	\
+	} while (0)
+
+#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
+			 ((u32) (a)[2]))
+#define WPA_PUT_BE24(a, val)					\
+	do {							\
+		(a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
+		(a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
+		(a)[2] = (u8) (((u32) (val)) & 0xff);		\
+	} while (0)
+
+#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
+			 (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
+#define WPA_PUT_BE32(a, val)					\
+	do {							\
+		(a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff);	\
+		(a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
+		(a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
+		(a)[3] = (u8) (((u32) (val)) & 0xff);		\
+	} while (0)
+
+#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \
+			 (((u32) (a)[1]) << 8) | ((u32) (a)[0]))
+#define WPA_PUT_LE32(a, val)					\
+	do {							\
+		(a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff);	\
+		(a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
+		(a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
+		(a)[0] = (u8) (((u32) (val)) & 0xff);		\
+	} while (0)
+
+#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \
+			 (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \
+			 (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \
+			 (((u64) (a)[6]) << 8) | ((u64) (a)[7]))
+#define WPA_PUT_BE64(a, val)				\
+	do {						\
+		(a)[0] = (u8) (((u64) (val)) >> 56);	\
+		(a)[1] = (u8) (((u64) (val)) >> 48);	\
+		(a)[2] = (u8) (((u64) (val)) >> 40);	\
+		(a)[3] = (u8) (((u64) (val)) >> 32);	\
+		(a)[4] = (u8) (((u64) (val)) >> 24);	\
+		(a)[5] = (u8) (((u64) (val)) >> 16);	\
+		(a)[6] = (u8) (((u64) (val)) >> 8);	\
+		(a)[7] = (u8) (((u64) (val)) & 0xff);	\
+	} while (0)
+
+#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \
+			 (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \
+			 (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \
+			 (((u64) (a)[1]) << 8) | ((u64) (a)[0]))
+
+
+/* Packing structures to avoid padding */
+#ifdef __GNUC__
+#define STRUCT_PACKED __attribute__ ((packed))
+#else
+#define STRUCT_PACKED
+#endif
+
+/* For md5.c file */
+#define INTERNAL_MD5
+#define CONFIG_CRYPTO_INTERNAL
+
+/* For radius.c file */
+#define CONFIG_IPV6
+#include <arpa/inet.h>
+
+
+#endif /* _HOSTAP_COMPAT_H */
Index: extensions/app_radgw/md5.c
===================================================================
--- extensions/app_radgw/md5.c	(revision 254)
+++ extensions/app_radgw/md5.c	(revision 254)
@@ -0,0 +1,401 @@
+/*********************************************************************************/
+/* freeDiameter author note:
+ *  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.
+ */
+#include "rgw_common.h"
+/* Forward declaration: */
+void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+/*********************************************************************************/
+
+ 
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+/**
+ * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (16 bytes)
+ */
+void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	u8 k_pad[64]; /* padding - key XORd with ipad/opad */
+	u8 tk[16];
+	const u8 *_addr[6];
+	size_t i, _len[6];
+
+	if (num_elem > 5) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return;
+	}
+
+        /* if key is longer than 64 bytes reset it to key = MD5(key) */
+        if (key_len > 64) {
+		md5_vector(1, &key, &key_len, tk);
+		key = tk;
+		key_len = 16;
+        }
+
+	/* the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+
+	/* XOR key with ipad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner MD5 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	md5_vector(1 + num_elem, _addr, _len, mac);
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer MD5 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	_addr[1] = mac;
+	_len[1] = MD5_MAC_LEN;
+	md5_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (16 bytes)
+ */
+void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac)
+{
+	hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef INTERNAL_MD5
+
+struct MD5Context {
+	u32 buf[4];
+	u32 bits[2];
+	u8 in[64];
+};
+
+#ifndef CONFIG_CRYPTO_INTERNAL
+static void MD5Init(struct MD5Context *context);
+static void MD5Update(struct MD5Context *context, unsigned char const *buf,
+			  unsigned len);
+static void MD5Final(unsigned char digest[16], struct MD5Context *context);
+#endif /* CONFIG_CRYPTO_INTERNAL */
+static void MD5Transform(u32 buf[4], u32 const in[16]);
+
+
+typedef struct MD5Context MD5_CTX;
+
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ */
+void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	MD5_CTX ctx;
+	size_t i;
+
+	MD5Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		MD5Update(&ctx, addr[i], len[i]);
+	MD5Final(mac, &ctx);
+}
+
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef WORDS_BIGENDIAN
+#define byteReverse(buf, len)	/* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    u32 t;
+    do {
+	t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(u32 *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    u32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    os_memcpy(p, buf, len);
+	    return;
+	}
+	os_memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	os_memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    os_memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	os_memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	os_memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	os_memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((u32 *) ctx->in)[14] = ctx->bits[0];
+    ((u32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (u32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    os_memcpy(digest, ctx->buf, 16);
+    os_memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(u32 buf[4], u32 const in[16])
+{
+    register u32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+/* ===== end - public domain MD5 implementation ===== */
+
+#endif /* INTERNAL_MD5 */
Index: extensions/app_radgw/md5.h
===================================================================
--- extensions/app_radgw/md5.h	(revision 254)
+++ extensions/app_radgw/md5.h	(revision 254)
@@ -0,0 +1,43 @@
+/*********************************************************************************/
+/* freeDiameter author note:
+ *  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.
+ */
+/*********************************************************************************/
+
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define MD5_MAC_LEN 16
+
+void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac);
+void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac);
+
+#ifdef CONFIG_CRYPTO_INTERNAL
+struct MD5Context;
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+	       unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+#endif /* CONFIG_CRYPTO_INTERNAL */
+
+#endif /* MD5_H */
Index: extensions/app_radgw/radius.c
===================================================================
--- extensions/app_radgw/radius.c	(revision 254)
+++ extensions/app_radgw/radius.c	(revision 254)
@@ -0,0 +1,1282 @@
+/*********************************************************************************/
+/* freeDiameter author note:
+ *  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.
+ */
+#include "rgw_common.h"
+
+/* Overwrite printf */
+#define printf(args...) fd_log_debug(args)
+
+/*********************************************************************************/
+
+
+/*
+ * hostapd / RADIUS message processing
+ * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+static struct radius_attr_hdr *
+radius_get_attr_hdr(struct radius_msg *msg, int idx)
+{
+	return (struct radius_attr_hdr *) (msg->buf + msg->attr_pos[idx]);
+}
+
+
+struct radius_msg *radius_msg_new(u8 code, u8 identifier)
+{
+	struct radius_msg *msg;
+
+	msg = os_malloc(sizeof(*msg));
+	if (msg == NULL)
+		return NULL;
+
+	if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) {
+		os_free(msg);
+		return NULL;
+	}
+
+	radius_msg_set_hdr(msg, code, identifier);
+
+	return msg;
+}
+
+
+int radius_msg_initialize(struct radius_msg *msg, size_t init_len)
+{
+	if (msg == NULL || init_len < sizeof(struct radius_hdr))
+		return -1;
+
+	os_memset(msg, 0, sizeof(*msg));
+	msg->buf = os_zalloc(init_len);
+	if (msg->buf == NULL)
+		return -1;
+
+	msg->buf_size = init_len;
+	msg->hdr = (struct radius_hdr *) msg->buf;
+	msg->buf_used = sizeof(*msg->hdr);
+
+	msg->attr_pos =
+		os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
+	if (msg->attr_pos == NULL) {
+		os_free(msg->buf);
+		msg->buf = NULL;
+		msg->hdr = NULL;
+		return -1;
+	}
+
+	msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
+	msg->attr_used = 0;
+
+	return 0;
+}
+
+
+void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
+{
+	msg->hdr->code = code;
+	msg->hdr->identifier = identifier;
+}
+
+
+void radius_msg_free(struct radius_msg *msg)
+{
+	os_free(msg->buf);
+	msg->buf = NULL;
+	msg->hdr = NULL;
+	msg->buf_size = msg->buf_used = 0;
+
+	os_free(msg->attr_pos);
+	msg->attr_pos = NULL;
+	msg->attr_size = msg->attr_used = 0;
+}
+
+
+static const char *radius_code_string(u8 code)
+{
+	switch (code) {
+	case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
+	case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
+	case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
+	case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
+	case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
+	case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
+	case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
+	case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
+	case RADIUS_CODE_RESERVED: return "Reserved";
+	default: return "?Unknown?";
+	}
+}
+
+
+struct radius_attr_type {
+	u8 type;
+	char *name;
+	enum {
+		RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
+		RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
+	} data_type;
+};
+
+static struct radius_attr_type radius_attrs[] =
+{
+	{ RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
+	{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
+	  RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
+	  RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
+	  RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
+	  RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
+};
+#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
+
+
+static struct radius_attr_type *radius_get_attr_type(u8 type)
+{
+	size_t i;
+
+	for (i = 0; i < RADIUS_ATTRS; i++) {
+		if (type == radius_attrs[i].type)
+			return &radius_attrs[i];
+	}
+
+	return NULL;
+}
+
+
+static void print_char(char c)
+{
+	if (c >= 32 && c < 127)
+		printf("%c", c);
+	else
+		printf("<%02x>", c);
+}
+
+
+static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
+{
+	struct radius_attr_type *attr;
+	int i, len;
+	unsigned char *pos;
+
+	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;
+
+	len = hdr->length - sizeof(struct radius_attr_hdr);
+	pos = (unsigned char *) (hdr + 1);
+
+	switch (attr->data_type) {
+	case RADIUS_ATTR_TEXT:
+		printf("      Value: '");
+		for (i = 0; i < len; i++)
+			print_char(pos[i]);
+		printf("'\n");
+		break;
+
+	case RADIUS_ATTR_IP:
+		if (len == 4) {
+			struct in_addr addr;
+			os_memcpy(&addr, pos, 4);
+			printf("      Value: %s\n", inet_ntoa(addr));
+		} else
+			printf("      Invalid IP address length %d\n", len);
+		break;
+
+#ifdef CONFIG_IPV6
+	case RADIUS_ATTR_IPV6:
+		if (len == 16) {
+			char buf[128];
+			const char *atxt;
+			struct in6_addr *addr = (struct in6_addr *) pos;
+			atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+			printf("      Value: %s\n", atxt ? atxt : "?");
+		} else
+			printf("      Invalid IPv6 address length %d\n", len);
+		break;
+#endif /* CONFIG_IPV6 */
+
+	case RADIUS_ATTR_HEXDUMP:
+	case RADIUS_ATTR_UNDIST:
+		printf("      Value:");
+		for (i = 0; i < len; i++)
+			printf(" %02x", pos[i]);
+		printf("\n");
+		break;
+
+	case RADIUS_ATTR_INT32:
+		if (len == 4)
+			printf("      Value: %u\n", WPA_GET_BE32(pos));
+		else
+			printf("      Invalid INT32 length %d\n", len);
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+void radius_msg_dump(struct radius_msg *msg)
+{
+	size_t i;
+
+	printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
+	       msg->hdr->code, radius_code_string(msg->hdr->code),
+	       msg->hdr->identifier, ntohs(msg->hdr->length));
+
+	for (i = 0; i < msg->attr_used; i++) {
+		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+		radius_msg_dump_attr(attr);
+	}
+}
+
+
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len)
+{
+	if (secret) {
+		u8 auth[MD5_MAC_LEN];
+		struct radius_attr_hdr *attr;
+
+		os_memset(auth, 0, MD5_MAC_LEN);
+		attr = radius_msg_add_attr(msg,
+					   RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+					   auth, MD5_MAC_LEN);
+		if (attr == NULL) {
+			printf("WARNING: Could not add "
+			       "Message-Authenticator\n");
+			return -1;
+		}
+		msg->hdr->length = htons(msg->buf_used);
+		hmac_md5(secret, secret_len, msg->buf, msg->buf_used,
+			 (u8 *) (attr + 1));
+	} else
+		msg->hdr->length = htons(msg->buf_used);
+
+	if (msg->buf_used > 0xffff) {
+		printf("WARNING: too long RADIUS message (%lu)\n",
+		       (unsigned long) msg->buf_used);
+		return -1;
+	}
+	return 0;
+}
+
+
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+			  size_t secret_len, const u8 *req_authenticator)
+{
+	u8 auth[MD5_MAC_LEN];
+	struct radius_attr_hdr *attr;
+	const u8 *addr[4];
+	size_t len[4];
+
+	os_memset(auth, 0, MD5_MAC_LEN);
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+				   auth, MD5_MAC_LEN);
+	if (attr == NULL) {
+		printf("WARNING: Could not add Message-Authenticator\n");
+		return -1;
+	}
+	msg->hdr->length = htons(msg->buf_used);
+	os_memcpy(msg->hdr->authenticator, req_authenticator,
+		  sizeof(msg->hdr->authenticator));
+	hmac_md5(secret, secret_len, msg->buf, msg->buf_used,
+		 (u8 *) (attr + 1));
+
+	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = 1 + 1 + 2;
+	addr[1] = req_authenticator;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = (u8 *) (msg->hdr + 1);
+	len[2] = msg->buf_used - sizeof(*msg->hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, msg->hdr->authenticator);
+
+	if (msg->buf_used > 0xffff) {
+		printf("WARNING: too long RADIUS message (%lu)\n",
+		       (unsigned long) msg->buf_used);
+		return -1;
+	}
+	return 0;
+}
+
+
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+			    size_t secret_len)
+{
+	const u8 *addr[2];
+	size_t len[2];
+
+	msg->hdr->length = htons(msg->buf_used);
+	os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
+	addr[0] = msg->buf;
+	len[0] = msg->buf_used;
+	addr[1] = secret;
+	len[1] = secret_len;
+	md5_vector(2, addr, len, msg->hdr->authenticator);
+
+	if (msg->buf_used > 0xffff) {
+		printf("WARNING: too long RADIUS messages (%lu)\n",
+		       (unsigned long) msg->buf_used);
+	}
+}
+
+
+static int radius_msg_add_attr_to_array(struct radius_msg *msg,
+					struct radius_attr_hdr *attr)
+{
+	if (msg->attr_used >= msg->attr_size) {
+		size_t *nattr_pos;
+		int nlen = msg->attr_size * 2;
+
+		nattr_pos = os_realloc(msg->attr_pos,
+				       nlen * sizeof(*msg->attr_pos));
+		if (nattr_pos == NULL)
+			return -1;
+
+		msg->attr_pos = nattr_pos;
+		msg->attr_size = nlen;
+	}
+
+	msg->attr_pos[msg->attr_used++] = (unsigned char *) attr - msg->buf;
+
+	return 0;
+}
+
+
+struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
+					    const u8 *data, size_t data_len)
+{
+	size_t buf_needed;
+	struct radius_attr_hdr *attr;
+
+	if (data_len > RADIUS_MAX_ATTR_LEN) {
+		printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
+		       (unsigned long) data_len);
+		return NULL;
+	}
+
+	buf_needed = msg->buf_used + sizeof(*attr) + data_len;
+
+	if (msg->buf_size < buf_needed) {
+		/* allocate more space for message buffer */
+		unsigned char *nbuf;
+		size_t nlen = msg->buf_size;
+
+		while (nlen < buf_needed)
+			nlen *= 2;
+		nbuf = os_realloc(msg->buf, nlen);
+		if (nbuf == NULL)
+			return NULL;
+		msg->buf = nbuf;
+		msg->hdr = (struct radius_hdr *) msg->buf;
+		os_memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size);
+		msg->buf_size = nlen;
+	}
+
+	attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used);
+	attr->type = type;
+	attr->length = sizeof(*attr) + data_len;
+	if (data_len > 0)
+		os_memcpy(attr + 1, data, data_len);
+
+	msg->buf_used += sizeof(*attr) + data_len;
+
+	if (radius_msg_add_attr_to_array(msg, attr))
+		return NULL;
+
+	return attr;
+}
+
+
+struct radius_msg *radius_msg_parse(const u8 *data, size_t len)
+{
+	struct radius_msg *msg;
+	struct radius_hdr *hdr;
+	struct radius_attr_hdr *attr;
+	size_t msg_len;
+	unsigned char *pos, *end;
+
+	if (data == NULL || len < sizeof(*hdr))
+		return NULL;
+
+	hdr = (struct radius_hdr *) data;
+
+	msg_len = ntohs(hdr->length);
+	if (msg_len < sizeof(*hdr) || msg_len > len) {
+		printf("Invalid RADIUS message length\n");
+		return NULL;
+	}
+
+	if (msg_len < len) {
+		printf("Ignored %lu extra bytes after RADIUS message\n",
+		       (unsigned long) len - msg_len);
+	}
+
+	msg = os_malloc(sizeof(*msg));
+	if (msg == NULL)
+		return NULL;
+
+	if (radius_msg_initialize(msg, msg_len)) {
+		os_free(msg);
+		return NULL;
+	}
+
+	os_memcpy(msg->buf, data, msg_len);
+	msg->buf_size = msg->buf_used = msg_len;
+
+	/* parse attributes */
+	pos = (unsigned char *) (msg->hdr + 1);
+	end = msg->buf + msg->buf_used;
+	while (pos < end) {
+		if ((size_t) (end - pos) < sizeof(*attr))
+			goto fail;
+
+		attr = (struct radius_attr_hdr *) pos;
+
+		if (pos + attr->length > end || attr->length < sizeof(*attr))
+			goto fail;
+
+		/* TODO: check that attr->length is suitable for attr->type */
+
+		if (radius_msg_add_attr_to_array(msg, attr))
+			goto fail;
+
+		pos += attr->length;
+	}
+
+	return msg;
+
+ fail:
+	radius_msg_free(msg);
+	os_free(msg);
+	return NULL;
+}
+
+
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
+{
+	const u8 *pos = data;
+	size_t left = data_len;
+
+	while (left > 0) {
+		int len;
+		if (left > RADIUS_MAX_ATTR_LEN)
+			len = RADIUS_MAX_ATTR_LEN;
+		else
+			len = left;
+
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
+					 pos, len))
+			return 0;
+
+		pos += len;
+		left -= len;
+	}
+
+	return 1;
+}
+
+
+u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
+{
+	u8 *eap, *pos;
+	size_t len, i;
+	struct radius_attr_hdr *attr;
+
+	if (msg == NULL)
+		return NULL;
+
+	len = 0;
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+		if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
+			len += attr->length - sizeof(struct radius_attr_hdr);
+	}
+
+	if (len == 0)
+		return NULL;
+
+	eap = os_malloc(len);
+	if (eap == NULL)
+		return NULL;
+
+	pos = eap;
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+		if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
+			int flen = attr->length - sizeof(*attr);
+			os_memcpy(pos, attr + 1, flen);
+			pos += flen;
+		}
+	}
+
+	if (eap_len)
+		*eap_len = len;
+
+	return eap;
+}
+
+
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len, const u8 *req_auth)
+{
+	u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+	u8 orig_authenticator[16];
+	struct radius_attr_hdr *attr = NULL, *tmp;
+	size_t i;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+			if (attr != NULL) {
+				printf("Multiple Message-Authenticator "
+				       "attributes in RADIUS message\n");
+				return 1;
+			}
+			attr = tmp;
+		}
+	}
+
+	if (attr == NULL) {
+		printf("No Message-Authenticator attribute found\n");
+		return 1;
+	}
+
+	os_memcpy(orig, attr + 1, MD5_MAC_LEN);
+	os_memset(attr + 1, 0, MD5_MAC_LEN);
+	if (req_auth) {
+		os_memcpy(orig_authenticator, msg->hdr->authenticator,
+			  sizeof(orig_authenticator));
+		os_memcpy(msg->hdr->authenticator, req_auth,
+			  sizeof(msg->hdr->authenticator));
+	}
+	hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth);
+	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
+	if (req_auth) {
+		os_memcpy(msg->hdr->authenticator, orig_authenticator,
+			  sizeof(orig_authenticator));
+	}
+
+	if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
+		printf("Invalid Message-Authenticator!\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len, struct radius_msg *sent_msg, int auth)
+{
+	const u8 *addr[4];
+	size_t len[4];
+	u8 hash[MD5_MAC_LEN];
+
+	if (sent_msg == NULL) {
+		printf("No matching Access-Request message found\n");
+		return 1;
+	}
+
+	if (auth &&
+	    radius_msg_verify_msg_auth(msg, secret, secret_len,
+				       sent_msg->hdr->authenticator)) {
+		return 1;
+	}
+
+	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = 1 + 1 + 2;
+	addr[1] = sent_msg->hdr->authenticator;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = (u8 *) (msg->hdr + 1);
+	len[2] = msg->buf_used - sizeof(*msg->hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, hash);
+	if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+		printf("Response Authenticator invalid!\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+			 u8 type)
+{
+	struct radius_attr_hdr *attr;
+	size_t i;
+	int count = 0;
+
+	for (i = 0; i < src->attr_used; i++) {
+		attr = radius_get_attr_hdr(src, i);
+		if (attr->type == type) {
+			if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
+						 attr->length - sizeof(*attr)))
+				return -1;
+			count++;
+		}
+	}
+
+	return count;
+}
+
+
+/* Create Request Authenticator. The value should be unique over the lifetime
+ * of the shared secret between authenticator and authentication server.
+ * Use one-way MD5 hash calculated from current timestamp and some data given
+ * by the caller. */
+void radius_msg_make_authenticator(struct radius_msg *msg,
+				   const u8 *data, size_t len)
+{
+	struct os_time tv;
+	long int l;
+	const u8 *addr[3];
+	size_t elen[3];
+
+	os_get_time(&tv);
+	l = os_random();
+	addr[0] = (u8 *) &tv;
+	elen[0] = sizeof(tv);
+	addr[1] = data;
+	elen[1] = len;
+	addr[2] = (u8 *) &l;
+	elen[2] = sizeof(l);
+	md5_vector(3, addr, elen, msg->hdr->authenticator);
+}
+
+
+/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
+ * Returns the Attribute payload and sets alen to indicate the length of the
+ * payload if a vendor attribute with subtype is found, otherwise returns NULL.
+ * The returned payload is allocated with os_malloc() and caller must free it
+ * by calling os_free().
+ */
+static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
+				      u8 subtype, size_t *alen)
+{
+	u8 *data, *pos;
+	size_t i, len;
+
+	if (msg == NULL)
+		return NULL;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+		size_t left;
+		u32 vendor_id;
+		struct radius_attr_vendor *vhdr;
+
+		if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
+			continue;
+
+		left = attr->length - sizeof(*attr);
+		if (left < 4)
+			continue;
+
+		pos = (u8 *) (attr + 1);
+
+		os_memcpy(&vendor_id, pos, 4);
+		pos += 4;
+		left -= 4;
+
+		if (ntohl(vendor_id) != vendor)
+			continue;
+
+		while (left >= sizeof(*vhdr)) {
+			vhdr = (struct radius_attr_vendor *) pos;
+			if (vhdr->vendor_length > left ||
+			    vhdr->vendor_length < sizeof(*vhdr)) {
+				left = 0;
+				break;
+			}
+			if (vhdr->vendor_type != subtype) {
+				pos += vhdr->vendor_length;
+				left -= vhdr->vendor_length;
+				continue;
+			}
+
+			len = vhdr->vendor_length - sizeof(*vhdr);
+			data = os_malloc(len);
+			if (data == NULL)
+				return NULL;
+			os_memcpy(data, pos + sizeof(*vhdr), len);
+			if (alen)
+				*alen = len;
+			return data;
+		}
+	}
+
+	return NULL;
+}
+
+
+static u8 * decrypt_ms_key(const u8 *key, size_t len,
+			   const u8 *req_authenticator,
+			   const u8 *secret, size_t secret_len, size_t *reslen)
+{
+	u8 *plain, *ppos, *res;
+	const u8 *pos;
+	size_t left, plen;
+	u8 hash[MD5_MAC_LEN];
+	int i, first = 1;
+	const u8 *addr[3];
+	size_t elen[3];
+
+	/* key: 16-bit salt followed by encrypted key info */
+
+	if (len < 2 + 16)
+		return NULL;
+
+	pos = key + 2;
+	left = len - 2;
+	if (left % 16) {
+		printf("Invalid ms key len %lu\n", (unsigned long) left);
+		return NULL;
+	}
+
+	plen = left;
+	ppos = plain = os_malloc(plen);
+	if (plain == NULL)
+		return NULL;
+	plain[0] = 0;
+
+	while (left > 0) {
+		/* b(1) = MD5(Secret + Request-Authenticator + Salt)
+		 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+
+		addr[0] = secret;
+		elen[0] = secret_len;
+		if (first) {
+			addr[1] = req_authenticator;
+			elen[1] = MD5_MAC_LEN;
+			addr[2] = key;
+			elen[2] = 2; /* Salt */
+		} else {
+			addr[1] = pos - MD5_MAC_LEN;
+			elen[1] = MD5_MAC_LEN;
+		}
+		md5_vector(first ? 3 : 2, addr, elen, hash);
+		first = 0;
+
+		for (i = 0; i < MD5_MAC_LEN; i++)
+			*ppos++ = *pos++ ^ hash[i];
+		left -= MD5_MAC_LEN;
+	}
+
+	if (plain[0] == 0 || plain[0] > plen - 1) {
+		printf("Failed to decrypt MPPE key\n");
+		os_free(plain);
+		return NULL;
+	}
+
+	res = os_malloc(plain[0]);
+	if (res == NULL) {
+		os_free(plain);
+		return NULL;
+	}
+	os_memcpy(res, plain + 1, plain[0]);
+	if (reslen)
+		*reslen = plain[0];
+	os_free(plain);
+	return res;
+}
+
+
+static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
+			   const u8 *req_authenticator,
+			   const u8 *secret, size_t secret_len,
+			   u8 *ebuf, size_t *elen)
+{
+	int i, len, first = 1;
+	u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
+	const u8 *addr[3];
+	size_t _len[3];
+
+	WPA_PUT_BE16(saltbuf, salt);
+
+	len = 1 + key_len;
+	if (len & 0x0f) {
+		len = (len & 0xf0) + 16;
+	}
+	os_memset(ebuf, 0, len);
+	ebuf[0] = key_len;
+	os_memcpy(ebuf + 1, key, key_len);
+
+	*elen = len;
+
+	pos = ebuf;
+	while (len > 0) {
+		/* b(1) = MD5(Secret + Request-Authenticator + Salt)
+		 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+		addr[0] = secret;
+		_len[0] = secret_len;
+		if (first) {
+			addr[1] = req_authenticator;
+			_len[1] = MD5_MAC_LEN;
+			addr[2] = saltbuf;
+			_len[2] = sizeof(saltbuf);
+		} else {
+			addr[1] = pos - MD5_MAC_LEN;
+			_len[1] = MD5_MAC_LEN;
+		}
+		md5_vector(first ? 3 : 2, addr, _len, hash);
+		first = 0;
+
+		for (i = 0; i < MD5_MAC_LEN; i++)
+			*pos++ ^= hash[i];
+
+		len -= MD5_MAC_LEN;
+	}
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+		       const u8 *secret, size_t secret_len)
+{
+	u8 *key;
+	size_t keylen;
+	struct radius_ms_mppe_keys *keys;
+
+	if (msg == NULL || sent_msg == NULL)
+		return NULL;
+
+	keys = os_zalloc(sizeof(*keys));
+	if (keys == NULL)
+		return NULL;
+
+	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+					 RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
+					 &keylen);
+	if (key) {
+		keys->send = decrypt_ms_key(key, keylen,
+					    sent_msg->hdr->authenticator,
+					    secret, secret_len,
+					    &keys->send_len);
+		os_free(key);
+	}
+
+	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+					 RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
+					 &keylen);
+	if (key) {
+		keys->recv = decrypt_ms_key(key, keylen,
+					    sent_msg->hdr->authenticator,
+					    secret, secret_len,
+					    &keys->recv_len);
+		os_free(key);
+	}
+
+	return keys;
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+			  const u8 *secret, size_t secret_len)
+{
+	u8 *key;
+	size_t keylen;
+	struct radius_ms_mppe_keys *keys;
+
+	if (msg == NULL || sent_msg == NULL)
+		return NULL;
+
+	keys = os_zalloc(sizeof(*keys));
+	if (keys == NULL)
+		return NULL;
+
+	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
+					 RADIUS_CISCO_AV_PAIR, &keylen);
+	if (key && keylen == 51 &&
+	    os_memcmp(key, "leap:session-key=", 17) == 0) {
+		keys->recv = decrypt_ms_key(key + 17, keylen - 17,
+					    sent_msg->hdr->authenticator,
+					    secret, secret_len,
+					    &keys->recv_len);
+	}
+	os_free(key);
+
+	return keys;
+}
+
+
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+			     const u8 *req_authenticator,
+			     const u8 *secret, size_t secret_len,
+			     const u8 *send_key, size_t send_key_len,
+			     const u8 *recv_key, size_t recv_key_len)
+{
+	struct radius_attr_hdr *attr;
+	u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
+	u8 *buf;
+	struct radius_attr_vendor *vhdr;
+	u8 *pos;
+	size_t elen;
+	int hlen;
+	u16 salt;
+
+	hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
+
+	/* MS-MPPE-Send-Key */
+	buf = os_malloc(hlen + send_key_len + 16);
+	if (buf == NULL) {
+		return 0;
+	}
+	pos = buf;
+	os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+	pos += sizeof(vendor_id);
+	vhdr = (struct radius_attr_vendor *) pos;
+	vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
+	pos = (u8 *) (vhdr + 1);
+	salt = os_random() | 0x8000;
+	WPA_PUT_BE16(pos, salt);
+	pos += 2;
+	encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
+		       secret_len, pos, &elen);
+	vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+				   buf, hlen + elen);
+	os_free(buf);
+	if (attr == NULL) {
+		return 0;
+	}
+
+	/* MS-MPPE-Recv-Key */
+	buf = os_malloc(hlen + send_key_len + 16);
+	if (buf == NULL) {
+		return 0;
+	}
+	pos = buf;
+	os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+	pos += sizeof(vendor_id);
+	vhdr = (struct radius_attr_vendor *) pos;
+	vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
+	pos = (u8 *) (vhdr + 1);
+	salt ^= 1;
+	WPA_PUT_BE16(pos, salt);
+	pos += 2;
+	encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
+		       secret_len, pos, &elen);
+	vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+				   buf, hlen + elen);
+	os_free(buf);
+	if (attr == NULL) {
+		return 0;
+	}
+
+	return 1;
+}
+
+
+/* Add User-Password attribute to a RADIUS message and encrypt it as specified
+ * in RFC 2865, Chap. 5.2 */
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+				  const u8 *data, size_t data_len,
+				  const u8 *secret, size_t secret_len)
+{
+	u8 buf[128];
+	int padlen, i;
+	size_t buf_len, pos;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 hash[16];
+
+	if (data_len > 128)
+		return NULL;
+
+	os_memcpy(buf, data, data_len);
+	buf_len = data_len;
+
+	padlen = data_len % 16;
+	if (padlen) {
+		padlen = 16 - padlen;
+		os_memset(buf + data_len, 0, padlen);
+		buf_len += padlen;
+	}
+
+	addr[0] = secret;
+	len[0] = secret_len;
+	addr[1] = msg->hdr->authenticator;
+	len[1] = 16;
+	md5_vector(2, addr, len, hash);
+
+	for (i = 0; i < 16; i++)
+		buf[i] ^= hash[i];
+	pos = 16;
+
+	while (pos < buf_len) {
+		addr[0] = secret;
+		len[0] = secret_len;
+		addr[1] = &buf[pos - 16];
+		len[1] = 16;
+		md5_vector(2, addr, len, hash);
+
+		for (i = 0; i < 16; i++)
+			buf[pos + i] ^= hash[i];
+
+		pos += 16;
+	}
+
+	return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
+				   buf, buf_len);
+}
+
+
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
+{
+	struct radius_attr_hdr *attr = NULL, *tmp;
+	size_t i, dlen;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == type) {
+			attr = tmp;
+			break;
+		}
+	}
+
+	if (!attr)
+		return -1;
+
+	dlen = attr->length - sizeof(*attr);
+	if (buf)
+		os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
+	return dlen;
+}
+
+
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+			    size_t *len, const u8 *start)
+{
+	size_t i;
+	struct radius_attr_hdr *attr = NULL, *tmp;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == type &&
+		    (start == NULL || (u8 *) tmp > start)) {
+			attr = tmp;
+			break;
+		}
+	}
+
+	if (!attr)
+		return -1;
+
+	*buf = (u8 *) (attr + 1);
+	*len = attr->length - sizeof(*attr);
+	return 0;
+}
+
+
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
+{
+	size_t i;
+	int count;
+
+	for (count = 0, i = 0; i < msg->attr_used; i++) {
+		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+		if (attr->type == type &&
+		    attr->length >= sizeof(struct radius_attr_hdr) + min_len)
+			count++;
+	}
+
+	return count;
+}
+
+
+struct radius_tunnel_attrs {
+	int tag_used;
+	int type; /* Tunnel-Type */
+	int medium_type; /* Tunnel-Medium-Type */
+	int vlanid;
+};
+
+
+/**
+ * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
+ * @msg: RADIUS message
+ * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
+ */
+int radius_msg_get_vlanid(struct radius_msg *msg)
+{
+	struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
+	size_t i;
+	struct radius_attr_hdr *attr = NULL;
+	const u8 *data;
+	char buf[10];
+	size_t dlen;
+
+	os_memset(&tunnel, 0, sizeof(tunnel));
+
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+		data = (const u8 *) (attr + 1);
+		dlen = attr->length - sizeof(*attr);
+		if (attr->length < 3)
+			continue;
+		if (data[0] >= RADIUS_TUNNEL_TAGS)
+			tun = &tunnel[0];
+		else
+			tun = &tunnel[data[0]];
+
+		switch (attr->type) {
+		case RADIUS_ATTR_TUNNEL_TYPE:
+			if (attr->length != 6)
+				break;
+			tun->tag_used++;
+			tun->type = WPA_GET_BE24(data + 1);
+			break;
+		case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
+			if (attr->length != 6)
+				break;
+			tun->tag_used++;
+			tun->medium_type = WPA_GET_BE24(data + 1);
+			break;
+		case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+			if (data[0] < RADIUS_TUNNEL_TAGS) {
+				data++;
+				dlen--;
+			}
+			if (dlen >= sizeof(buf))
+				break;
+			os_memcpy(buf, data, dlen);
+			buf[dlen] = '\0';
+			tun->tag_used++;
+			tun->vlanid = atoi(buf);
+			break;
+		}
+	}
+
+	for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
+		tun = &tunnel[i];
+		if (tun->tag_used &&
+		    tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
+		    tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
+		    tun->vlanid > 0)
+			return tun->vlanid;
+	}
+
+	return -1;
+}
+
+
+void radius_free_class(struct radius_class_data *c)
+{
+	size_t i;
+	if (c == NULL)
+		return;
+	for (i = 0; i < c->count; i++)
+		os_free(c->attr[i].data);
+	os_free(c->attr);
+	c->attr = NULL;
+	c->count = 0;
+}
+
+
+int radius_copy_class(struct radius_class_data *dst,
+		      const struct radius_class_data *src)
+{
+	size_t i;
+
+	if (src->attr == NULL)
+		return 0;
+
+	dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
+	if (dst->attr == NULL)
+		return -1;
+
+	dst->count = 0;
+
+	for (i = 0; i < src->count; i++) {
+		dst->attr[i].data = os_malloc(src->attr[i].len);
+		if (dst->attr[i].data == NULL)
+			break;
+		dst->count++;
+		os_memcpy(dst->attr[i].data, src->attr[i].data,
+			  src->attr[i].len);
+		dst->attr[i].len = src->attr[i].len;
+	}
+
+	return 0;
+}
Index: extensions/app_radgw/radius.h
===================================================================
--- extensions/app_radgw/radius.h	(revision 254)
+++ extensions/app_radgw/radius.h	(revision 254)
@@ -0,0 +1,296 @@
+/*********************************************************************************/
+/* freeDiameter author note:
+ *  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.
+ */
+/*********************************************************************************/
+
+/*
+ * hostapd / RADIUS message processing
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RADIUS_H
+#define RADIUS_H
+
+/* RFC 2865 - RADIUS */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct radius_hdr {
+	u8 code;
+	u8 identifier;
+	u16 length; /* including this header */
+	u8 authenticator[16];
+	/* followed by length-20 octets of attributes */
+} STRUCT_PACKED;
+
+enum { RADIUS_CODE_ACCESS_REQUEST = 1,
+       RADIUS_CODE_ACCESS_ACCEPT = 2,
+       RADIUS_CODE_ACCESS_REJECT = 3,
+       RADIUS_CODE_ACCOUNTING_REQUEST = 4,
+       RADIUS_CODE_ACCOUNTING_RESPONSE = 5,
+       RADIUS_CODE_ACCESS_CHALLENGE = 11,
+       RADIUS_CODE_STATUS_SERVER = 12,
+       RADIUS_CODE_STATUS_CLIENT = 13,
+       RADIUS_CODE_RESERVED = 255
+};
+
+struct radius_attr_hdr {
+	u8 type;
+	u8 length; /* including this header */
+	/* followed by length-2 octets of attribute value */
+} STRUCT_PACKED;
+
+#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr))
+
+enum { RADIUS_ATTR_USER_NAME = 1,
+       RADIUS_ATTR_USER_PASSWORD = 2,
+       RADIUS_ATTR_NAS_IP_ADDRESS = 4,
+       RADIUS_ATTR_NAS_PORT = 5,
+       RADIUS_ATTR_FRAMED_MTU = 12,
+       RADIUS_ATTR_REPLY_MESSAGE = 18,
+       RADIUS_ATTR_STATE = 24,
+       RADIUS_ATTR_CLASS = 25,
+       RADIUS_ATTR_VENDOR_SPECIFIC = 26,
+       RADIUS_ATTR_SESSION_TIMEOUT = 27,
+       RADIUS_ATTR_IDLE_TIMEOUT = 28,
+       RADIUS_ATTR_TERMINATION_ACTION = 29,
+       RADIUS_ATTR_CALLED_STATION_ID = 30,
+       RADIUS_ATTR_CALLING_STATION_ID = 31,
+       RADIUS_ATTR_NAS_IDENTIFIER = 32,
+       RADIUS_ATTR_PROXY_STATE = 33,
+       RADIUS_ATTR_ACCT_STATUS_TYPE = 40,
+       RADIUS_ATTR_ACCT_DELAY_TIME = 41,
+       RADIUS_ATTR_ACCT_INPUT_OCTETS = 42,
+       RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43,
+       RADIUS_ATTR_ACCT_SESSION_ID = 44,
+       RADIUS_ATTR_ACCT_AUTHENTIC = 45,
+       RADIUS_ATTR_ACCT_SESSION_TIME = 46,
+       RADIUS_ATTR_ACCT_INPUT_PACKETS = 47,
+       RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48,
+       RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49,
+       RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50,
+       RADIUS_ATTR_ACCT_LINK_COUNT = 51,
+       RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52,
+       RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53,
+       RADIUS_ATTR_EVENT_TIMESTAMP = 55,
+       RADIUS_ATTR_NAS_PORT_TYPE = 61,
+       RADIUS_ATTR_TUNNEL_TYPE = 64,
+       RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65,
+       RADIUS_ATTR_CONNECT_INFO = 77,
+       RADIUS_ATTR_EAP_MESSAGE = 79,
+       RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80,
+       RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
+       RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
+       RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
+       RADIUS_ATTR_NAS_IPV6_ADDRESS = 95
+};
+
+
+/* Termination-Action */
+#define RADIUS_TERMINATION_ACTION_DEFAULT 0
+#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1
+
+/* NAS-Port-Type */
+#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19
+
+/* Acct-Status-Type */
+#define RADIUS_ACCT_STATUS_TYPE_START 1
+#define RADIUS_ACCT_STATUS_TYPE_STOP 2
+#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8
+
+/* Acct-Authentic */
+#define RADIUS_ACCT_AUTHENTIC_RADIUS 1
+#define RADIUS_ACCT_AUTHENTIC_LOCAL 2
+#define RADIUS_ACCT_AUTHENTIC_REMOTE 3
+
+/* Acct-Terminate-Cause */
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3
+#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4
+#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14
+#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15
+#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17
+#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18
+
+#define RADIUS_TUNNEL_TAGS 32
+
+/* Tunnel-Type */
+#define RADIUS_TUNNEL_TYPE_PPTP 1
+#define RADIUS_TUNNEL_TYPE_L2TP 3
+#define RADIUS_TUNNEL_TYPE_IPIP 7
+#define RADIUS_TUNNEL_TYPE_GRE 10
+#define RADIUS_TUNNEL_TYPE_VLAN 13
+
+/* Tunnel-Medium-Type */
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2
+#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6
+
+
+struct radius_attr_vendor {
+	u8 vendor_type;
+	u8 vendor_length;
+} STRUCT_PACKED;
+
+#define RADIUS_VENDOR_ID_CISCO 9
+#define RADIUS_CISCO_AV_PAIR 1
+
+/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+
+enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16,
+       RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
+};
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+struct radius_ms_mppe_keys {
+	u8 *send;
+	size_t send_len;
+	u8 *recv;
+	size_t recv_len;
+};
+
+
+/* RADIUS message structure for new and parsed messages */
+struct radius_msg {
+	unsigned char *buf;
+	size_t buf_size; /* total size allocated for buf */
+	size_t buf_used; /* bytes used in buf */
+
+	struct radius_hdr *hdr;
+
+	size_t *attr_pos; /* array of indexes to attributes (number of bytes
+			   * from buf to the beginning of
+			   * struct radius_attr_hdr). */
+	size_t attr_size; /* total size of the attribute pointer array */
+	size_t attr_used; /* total number of attributes in the array */
+};
+
+
+/* Default size to be allocated for new RADIUS messages */
+#define RADIUS_DEFAULT_MSG_SIZE 1024
+
+/* Default size to be allocated for attribute array */
+#define RADIUS_DEFAULT_ATTR_COUNT 16
+
+
+/* MAC address ASCII format for IEEE 802.1X use
+ * (draft-congdon-radius-8021x-20.txt) */
+#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X"
+/* MAC address ASCII format for non-802.1X use */
+#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct radius_msg *radius_msg_new(u8 code, u8 identifier);
+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(struct radius_msg *msg);
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len);
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+			  size_t secret_len, const u8 *req_authenticator);
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+			    size_t secret_len);
+struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
+					    const u8 *data, size_t data_len);
+struct radius_msg *radius_msg_parse(const u8 *data, size_t len);
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
+		       size_t data_len);
+u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len);
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len, struct radius_msg *sent_msg,
+		      int auth);
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len, const u8 *req_auth);
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+			 u8 type);
+void radius_msg_make_authenticator(struct radius_msg *msg,
+				   const u8 *data, size_t len);
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+		       const u8 *secret, size_t secret_len);
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+			  const u8 *secret, size_t secret_len);
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+			     const u8 *req_authenticator,
+			     const u8 *secret, size_t secret_len,
+			     const u8 *send_key, size_t send_key_len,
+			     const u8 *recv_key, size_t recv_key_len);
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+				  const u8 *data, size_t data_len,
+				  const u8 *secret, size_t secret_len);
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
+int radius_msg_get_vlanid(struct radius_msg *msg);
+
+static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
+					    u32 value)
+{
+	u32 val = htonl(value);
+	return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL;
+}
+
+static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type,
+					    u32 *value)
+{
+	u32 val;
+	int res;
+	res = radius_msg_get_attr(msg, type, (u8 *) &val, 4);
+	if (res != 4)
+		return -1;
+
+	*value = ntohl(val);
+	return 0;
+}
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+			    size_t *len, const u8 *start);
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len);
+
+
+struct radius_attr_data {
+	u8 *data;
+	size_t len;
+};
+
+struct radius_class_data {
+	struct radius_attr_data *attr;
+	size_t count;
+};
+
+void radius_free_class(struct radius_class_data *c);
+int radius_copy_class(struct radius_class_data *dst,
+		      const struct radius_class_data *src);
+
+#endif /* RADIUS_H */
Index: extensions/app_radgw/rgw.h
===================================================================
--- extensions/app_radgw/rgw.h	(revision 254)
+++ extensions/app_radgw/rgw.h	(revision 254)
@@ -0,0 +1,133 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* This file contains the definitions for the app_radgw internal use. */
+  
+#ifndef _RGW_H
+#define _RGW_H
+
+/* include the common definitions */
+#include "rgw_common.h"
+
+/* RADIUS messages + metadata */
+struct rgw_radius_msg_meta {
+
+	/* The RADIUS message */
+	struct radius_msg 	radius;
+	
+	/* Metadata */
+	struct {
+		/* The port it was sent from, in network byte order */
+		unsigned	port :16;
+		
+		/* received on ACCT or AUTH port? */
+		unsigned	serv_type :2;
+		
+		/* 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_radius_msg_meta * msg, struct rgw_client * cli, struct session ** session, struct msg ** diam);
+int rgw_msg_init(void);
+
+/* Local RADIUS server(s) configuration */
+struct rgw_serv {
+	unsigned	disabled	:1;
+	unsigned	ip_disabled	:1;
+	unsigned	ip6_disabled	:1;
+	unsigned	:13; /* padding */
+	
+	uint16_t	port;	/* stored in network byte order */
+	
+	struct in_addr	ip_endpoint;
+	struct in6_addr	ip6_endpoint;
+};
+
+extern struct rgw_servs {
+	struct rgw_serv	auth_serv;
+	struct rgw_serv	acct_serv;
+} rgw_servers;
+
+int rgw_servers_init(void);
+int rgw_servers_start(void);
+void rgw_servers_dump(void);
+int rgw_servers_send(int type, unsigned char *buf, size_t buflen, struct sockaddr *to, uint16_t to_port);
+void rgw_servers_fini(void);
+
+
+/* Clients management */
+int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen );
+int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len);
+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 **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);
+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);
+
+
+/* Management of plugins */
+int rgw_plg_add( char * plgfile, char * conffile, int port, unsigned char ** codes_array, size_t codes_sz );
+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);
+void rgw_plg_fini(void);
+
+
+/* Parse configuration file */
+int rgw_conf_handle(char * conffile);
+
+
+/* Worker module, process incoming RADIUS messages (in separated threads) */
+int rgw_work_start(void);
+int rgw_work_add(struct rgw_radius_msg_meta * msg, struct rgw_client * client);
+void rgw_work_fini(void);
+
+
+#endif /* _RGW_H */
+  
Index: extensions/app_radgw/rgw_clients.c
===================================================================
--- extensions/app_radgw/rgw_clients.c	(revision 254)
+++ extensions/app_radgw/rgw_clients.c	(revision 254)
@@ -0,0 +1,610 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Manage the list of RADIUS clients, along with their shared secrets. */
+
+/* Probably some changes are needed to support RADIUS Proxies */
+
+#include "rgw.h"
+
+/* 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.
+ */
+static struct fd_list cli_ip = FD_LIST_INITIALIZER(cli_ip);
+static struct fd_list cli_ip6 = FD_LIST_INITIALIZER(cli_ip6);
+
+/* Mutex to protect the previous lists */
+static pthread_mutex_t cli_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/* Structure describing one client */
+struct rgw_client {
+	/* Link information in global list */
+	struct fd_list		chain;
+	
+	/* Reference count */
+	int			refcount;
+	
+	/* The address and optional port (alloc'd during configuration file parsing). */
+	union {
+		struct sockaddr		*sa; /* generic pointer */
+		struct sockaddr_in	*sin;
+		struct sockaddr_in6	*sin6;
+	};
+	
+	/* The FQDN, realm, and optional aliases */
+	char 			*fqdn;
+	char			*realm;
+	char			**aliases;
+	size_t			 aliases_nb;
+	
+	/* The secret key data. */
+	struct {
+		unsigned char * data;
+		size_t		len;
+	} 			key;
+	
+	/* information of previous msg received, for duplicate checks. */
+	struct {
+		uint16_t		port;
+		uint8_t			id;
+		struct radius_msg * 	ans; /* to be able to resend a lost answer */
+	} last[2]; /*[0] for auth, [1] for acct. */
+};
+
+
+
+/* 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 )
+{
+	struct rgw_client *tmp = NULL;
+	char buf[255];
+	int ret;
+	
+	/* Search FQDN for the client */
+	ret = getnameinfo( *ip_port, sizeof(struct sockaddr_storage), &buf[0], sizeof(buf), NULL, 0, 0 );
+	if (ret) {
+		TRACE_DEBUG(INFO, "Unable to resolve peer name: %s", gai_strerror(ret));
+		return EINVAL;
+	}
+	
+	/* Create the new object */
+	CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) );
+	memset(tmp, 0, sizeof(struct rgw_client));
+	fd_list_init(&tmp->chain, NULL);
+	
+	/* Copy the fqdn */
+	CHECK_MALLOC( tmp->fqdn = strdup(buf) );
+	/* Find an appropriate realm */
+	tmp->realm = strchr(tmp->fqdn, '.');
+	if (tmp->realm)
+		tmp->realm += 1;
+	if ((!tmp->realm) || (*tmp->realm == '\0')) /* in case the fqdn was "localhost." for example, if it is possible... */
+		tmp->realm = fd_g_config->cnf_diamrlm;
+	
+	/* move the sa info reference */
+	tmp->sa = *ip_port;
+	*ip_port = NULL;
+	
+	/* move the key material */
+	tmp->key.data = *key;
+	tmp->key.len = keylen;
+	*key = NULL;
+	
+	/* Done! */
+	*res = tmp;
+	return 0;
+}
+
+
+/* Decrease refcount on a client; the lock must be held when this function is called. */
+static void client_unlink(struct rgw_client * client)
+{
+	client->refcount -= 1;
+	
+	if (client->refcount <= 0) {
+		int idx;
+		/* to be sure: the refcount should be 0 only when client_fini is called */
+		ASSERT( FD_IS_LIST_EMPTY(&client->chain) );
+		
+		/* Free the data */
+		for (idx = 0; idx < client->aliases_nb; idx++)
+			free(client->aliases[idx]);
+		free(client->aliases);
+		free(client->fqdn);
+		free(client->sa);
+		free(client->key.data);
+		free(client);
+	}
+}
+
+
+/* Macro to avoid duplicting the code in the next function */
+#define client_search_family( _family_ )												\
+		case AF_INET##_family_: {												\
+			struct sockaddr_in##_family_ * sin##_family_ = (struct sockaddr_in##_family_ *)ip_port;				\
+			for (ref = cli_ip##_family_.next; ref != &cli_ip##_family_; ref = ref->next) {					\
+				cmp = memcmp(&sin##_family_->sin##_family_##_addr, 							\
+					     &((struct rgw_client *)ref)->sin##_family_->sin##_family_##_addr, 				\
+					     sizeof(struct in##_family_##_addr));							\
+				if (cmp > 0) continue; /* search further in the list */							\
+				if (cmp < 0) break; /* this IP is not in the list */							\
+				/* Now compare the ports as follow: */									\
+				     /* If the ip_port we are searching does not contain a port, just return the first match result */	\
+				if ( (sin##_family_->sin##_family_##_port == 0) 							\
+				     /* If the entry in the list does not contain a port, return it as a match */			\
+				  || (((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port == 0) 				\
+				     /* If both ports are equal, it is a match */							\
+				  || (sin##_family_->sin##_family_##_port == 								\
+				  		((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port)) {			\
+					*res = (struct rgw_client *)ref;								\
+					return EEXIST;											\
+				}													\
+				/* Otherwise, the list is ordered by port value (byte order does not matter */				\
+				if (sin##_family_->sin##_family_##_port 								\
+					> ((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port) continue;			\
+				else break;												\
+			}														\
+			*res = (struct rgw_client *)(ref->prev);									\
+			return ENOENT;													\
+		}
+/* Function to look for an existing rgw_client, or the previous element. 
+   The cli_mtx must be held when calling this function. 
+   Returns ENOENT if the matching client does not exist, and res points to the previous element in the list. 
+   Returns EEXIST if the matching client is found, and res points to this element. 
+   Returns other error code on other error. */
+static int client_search(struct rgw_client ** res, struct sockaddr * ip_port )
+{
+	int ret = 0;
+	int cmp;
+	struct fd_list *ref = NULL;
+	
+	CHECK_PARAMS(res && ip_port);
+	
+	switch (ip_port->sa_family) {
+		client_search_family()
+				break;
+		
+		client_search_family( 6 )
+				break;
+	}
+	
+	/* We're never supposed to reach this point */
+	ASSERT(0);
+	return EINVAL;
+}
+
+int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len)
+{
+	CHECK_PARAMS( cli && key && key_len );
+	*key = cli->key.data;
+	*key_len = cli->key.len;
+	return 0;
+}
+
+int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref)
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %p", ip_port, ref);
+	
+	CHECK_PARAMS(ip_port && ref);
+	
+	CHECK_POSIX( pthread_mutex_lock(&cli_mtx) );
+
+	ret = client_search(ref, ip_port);
+	if (ret == EEXIST) {
+		(*ref)->refcount ++;
+		ret = 0;
+	} else {
+		*ref = NULL;
+	}
+	
+	CHECK_POSIX( pthread_mutex_unlock(&cli_mtx) );
+	
+	return ret;
+}
+
+int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli)
+{
+	int idx;
+	
+	TRACE_ENTRY("%p %p", msg, cli);
+	
+	CHECK_PARAMS( msg && cli );
+	
+	if ((*msg)->serv_type == RGW_PLG_TYPE_AUTH)
+		idx = 0;
+	else
+		idx = 1;
+	
+	if ((cli->last[idx].id == (*msg)->radius.hdr->identifier) && (cli->last[idx].port == (*msg)->port)) {
+		/* Duplicate! */
+		TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu).", (*msg)->radius.hdr->identifier, ntohs((*msg)->port));
+		if (cli->last[idx].ans) {
+			/* Resend the answer */
+			CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type, cli->last[idx].ans->buf, cli->last[idx].ans->buf_used, cli->sa, (*msg)->port),  );
+		}
+		rgw_msg_free(msg);
+	} else {
+		/* Update information for new message */
+		if (cli->last[idx].ans) {
+			/* Free it */
+			radius_msg_free(cli->last[idx].ans);
+			free(cli->last[idx].ans);
+			cli->last[idx].ans = NULL;
+		}
+		cli->last[idx].id = (*msg)->radius.hdr->identifier;
+		cli->last[idx].port = (*msg)->port;
+	}
+	
+	return 0;
+}
+
+/* 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;
+	struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL;
+	
+	TRACE_ENTRY("%p %p", msg, cli);
+	CHECK_PARAMS(msg && cli && !msg->valid_nas_info );
+		
+	/* 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]);
+		unsigned char * attr_val = (unsigned char *)(attr + 1);
+		size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
+		
+		if ((attr->type == RADIUS_ATTR_NAS_IP_ADDRESS) && (attr_len = 4)) {
+			nas_ip = attr;
+			continue;
+		}
+			
+		if ((attr->type == RADIUS_ATTR_NAS_IDENTIFIER) && (attr_len > 0)) {
+			nas_id = attr;
+			continue;
+		}
+			
+		if ((attr->type == RADIUS_ATTR_NAS_IPV6_ADDRESS) && (attr_len = 16)) {
+			nas_ip6 = attr;
+			continue;
+		}
+	}
+		
+	if (!nas_ip && !nas_ip6 && !nas_id) {
+		TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute.");
+		goto end;
+	}
+	
+	/* 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;
+	}
+	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;
+	}
+	
+	/* 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.
+
+				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;
+	}
+	
+	/* Now check the nas_id */
+	if (nas_id) {
+		/*
+			In RADIUS it would be possible for a rogue NAS to forge the NAS-
+			Identifier attribute.  Diameter/RADIUS translation agents SHOULD
+			attempt to check a received NAS-Identifier attribute against the
+			source address of the RADIUS packet, by doing an A/AAAA RR query.  If
+			the NAS-Identifier attribute contains an FQDN, then such a query
+			would resolve to an IP address matching the source address.  However,
+			the NAS-Identifier attribute is not required to contain an FQDN, so
+			such a query could fail.  If it fails, an error should be logged, but
+			no action should be taken, other than a reverse lookup on the source
+			address and insert the resulting FQDN into the Route-Record AVP.
+
+			Diameter agents and servers SHOULD check whether a NAS-Identifier AVP
+			corresponds to an entry in the Route-Record AVP.  If no match is
+			found, then an error is logged, but no other action is taken.
+		*/
+	
+		/* copy the alias */
+		char * str;
+		int found, ret;
+		struct addrinfo hint, *res;
+		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';
+		
+		/* Check if this alias is already in the aliases list */
+		if (!strcasecmp(str, cli->fqdn)) {
+			TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the NAS");
+			found = 1;
+		} else {
+			for (idx = 0; idx < cli->aliases_nb; idx++) {
+				if (!strcasecmp(str, cli->aliases[idx])) {
+					TRACE_DEBUG(FULL, "NAS-Identifier valid value found in the cache");
+					found = 1;
+					break;
+				}
+			}
+		}
+		
+		if (found) {
+			free(str);
+			msg->valid_nas_info |= 2;
+			goto end;
+		}
+		
+		/* 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);
+		if (ret) {
+			TRACE_DEBUG(INFO, "Error while resolving NAS-Identifier value '%s': %s. Discarding message...", str, gai_strerror(ret));
+			free(str);
+			return EINVAL;
+		}
+		if (strcasecmp(cli->fqdn, res->ai_canonname)) {
+			TRACE_DEBUG(INFO, "The NAS-Identifier value is not valid: '%s' resolved to '%s', expected '%s'. Discarding...", str, res->ai_canonname, cli->fqdn);
+			free(str);
+			freeaddrinfo(res);
+			return EINVAL;
+		}
+		
+		/* It is a valid alias, save it */
+		freeaddrinfo(res);
+		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:	
+	return 0;
+}
+
+int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm)
+{
+	TRACE_ENTRY("%p %p %p", cli, fqdn, realm);
+	CHECK_PARAMS(cli && fqdn);
+	
+	*fqdn = cli->fqdn;
+	if (realm)
+		*realm= cli->realm;
+	return 0;
+}
+
+
+void rgw_clients_dispose(struct rgw_client ** ref)
+{
+	TRACE_ENTRY("%p", ref);
+	CHECK_PARAMS_DO(ref, return);
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx),  );
+	client_unlink(*ref);
+	*ref = NULL;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), );
+}
+
+int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen )
+{
+	struct rgw_client * prev = NULL, *new = NULL;
+	int ret;
+	
+	TRACE_ENTRY("%p %p %lu", ip_port, key, keylen);
+	
+	CHECK_PARAMS( ip_port && key && *key && keylen );
+	CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) );
+	
+	/* Dump the entry in debug mode */
+	if (TRACE_BOOL(FULL + 1 )) {
+		TRACE_DEBUG(FULL, "Adding client:\n");
+		TRACE_DEBUG_sSA(FULL, 	 "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "\n" );
+		TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]\n" );
+	}
+	
+	/* Lock the lists */
+	CHECK_POSIX( pthread_mutex_lock(&cli_mtx) );
+	
+	/* Check if the same entry does not already exist */
+	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 );
+		fd_list_insert_after(&prev->chain, &new->chain);
+		new->refcount++;
+		ret = 0;
+		goto end;
+	}
+	
+	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) )) {
+			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:\n");
+		TRACE_DEBUG_sSA(NONE, 	 "\tIP : ", prev->sa, NI_NUMERICHOST | NI_NUMERICSERV, "\n" );
+		TRACE_DEBUG_BUFFER(NONE, "\tKey: [", prev->key.data, prev->key.len, "]\n" );
+		TRACE_DEBUG(NONE, "Conflicting entry:\n");
+		TRACE_DEBUG_sSA(NONE, 	 "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "\n" );
+		TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]\n" );
+	}
+end:
+	/* release the lists */
+	CHECK_POSIX( pthread_mutex_unlock(&cli_mtx) );
+	
+	return ret;
+}
+
+static void dump_cli_list(struct fd_list *senti)
+{
+	struct rgw_client * client = NULL;
+	struct fd_list *ref = NULL;
+	
+	for (ref = senti->next; ref != senti; ref = ref->next) {
+		client = (struct rgw_client *)ref;
+		TRACE_DEBUG_sSA(NONE, 	 "  - ", client->sa, NI_NUMERICHOST | NI_NUMERICSERV, "\n" );
+		TRACE_DEBUG_BUFFER(NONE, "     [", client->key.data, client->key.len, "]\n" );
+	}
+}
+
+void rgw_clients_dump(void)
+{
+	if ( ! TRACE_BOOL(FULL) )
+		return;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ );
+	
+	if (!FD_IS_LIST_EMPTY(&cli_ip))
+		fd_log_debug(" RADIUS IP clients list:\n");
+	dump_cli_list(&cli_ip);
+		
+	if (!FD_IS_LIST_EMPTY(&cli_ip6))
+		fd_log_debug(" RADIUS IPv6 clients list:\n");
+	dump_cli_list(&cli_ip6);
+		
+	CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), /* ignore error */ );
+}
+
+void rgw_clients_fini(void)
+{
+	struct fd_list * client;
+	
+	TRACE_ENTRY();
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ );
+	
+	/* empty the lists */
+	while ( ! FD_IS_LIST_EMPTY(&cli_ip) ) {
+		client = cli_ip.next;
+		fd_list_unlink(client);
+		client_unlink((struct rgw_client *)client);
+	}
+	while (! FD_IS_LIST_EMPTY(&cli_ip6)) {
+		client = cli_ip6.next;
+		fd_list_unlink(client);
+		client_unlink((struct rgw_client *)client);
+	}
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), /* ignore error */ );
+	
+}
+
+int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli)
+{
+	int idx;
+	
+	TRACE_ENTRY("%p %p %p", msg, req, cli);
+	CHECK_PARAMS( msg && *msg && cli );
+	
+	if (!req) {
+		/* We don't support this case yet */
+		ASSERT(0);
+		return ENOTSUP;
+	}
+	
+	if (radius_msg_finish_srv(*msg, cli->key.data, cli->key.len, req->radius.hdr->authenticator)) {
+		TRACE_DEBUG(INFO, "An error occurred while preparing the RADIUS answer");
+		radius_msg_free(*msg);
+		free(*msg);
+		*msg = NULL;
+		return EINVAL;
+	}
+	
+	/* Debug */
+	TRACE_DEBUG(FULL, "RADIUS message ready for sending:");
+	rgw_msg_dump((struct rgw_radius_msg_meta *)*msg);
+
+	/* Send the message */
+	CHECK_FCT( rgw_servers_send(req->serv_type, (*msg)->buf, (*msg)->buf_used, cli->sa, req->port) );
+
+	/* update the duplicate cache in rgw_clients */
+	if (req->serv_type == RGW_PLG_TYPE_AUTH)
+		idx = 0;
+	else
+		idx = 1;
+	if (cli->last[idx].ans) {
+		/* Free it */
+		radius_msg_free(cli->last[idx].ans);
+		free(cli->last[idx].ans);
+	}
+	cli->last[idx].ans = *msg;
+	*msg = NULL;
+	
+	/* Finished */
+	return 0;
+}
+
Index: extensions/app_radgw/rgw_common.h
===================================================================
--- extensions/app_radgw/rgw_common.h	(revision 254)
+++ extensions/app_radgw/rgw_common.h	(revision 254)
@@ -0,0 +1,222 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* This file contains definitions for both app_radgw extension and its plugins. */
+
+#ifndef _RGW_COMMON_H
+#define _RGW_COMMON_H
+
+/* Include definitions from the freeDiameter framework */
+#include <freeDiameter/extension.h>
+
+/* Include hostap files for RADIUS processings */
+#include "hostap_compat.h"
+#include "md5.h"
+#include "radius.h"
+
+
+/**************************************************************/
+/*              Interface with gateway's plug-ins             */
+/**************************************************************/
+/* This structure is private for each plugin */
+struct rgwp_config;
+
+/* This structure points to a RADIUS client description, the definition is not known to plugins */
+struct rgw_client;
+
+/* Each plugin must provide the following structure. */
+extern struct rgw_api {
+	/* Parse the configuration file. It may be called several times with different configurations.
+	    Returns NULL on errors.
+	    Called even if no configuration file is passed (with NULL parameter then) */
+	struct rgwp_config * (*rgwp_conf_parse) ( char * conf_file );
+	
+	/* Cleanup the configuration state when the daemon is exiting. */
+	void (*rgwp_conf_free) (struct rgwp_config * conf);
+
+	/* 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; 
+	   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 );
+	/* ret 0: continue; ret >0: error; ret: -1 ... (tbd) */
+
+} rgwp_descriptor;
+
+
+
+/**************************************************************/
+/*              Additional definitions                        */
+/**************************************************************/
+/* Type of message / server */
+#define RGW_PLG_TYPE_AUTH	1
+#define RGW_PLG_TYPE_ACCT	2
+
+/* Attributes missing from radius.h (not used in EAP) */
+enum { RADIUS_ATTR_CHAP_PASSWORD = 3,
+       RADIUS_ATTR_SERVICE_TYPE = 6,
+       RADIUS_ATTR_FRAMED_PROTOCOL = 7,
+       RADIUS_ATTR_FRAMED_IP_ADDRESS = 8,
+       RADIUS_ATTR_FRAMED_IP_NETMASK = 9,
+       RADIUS_ATTR_FRAMED_ROUTING = 10,
+       RADIUS_ATTR_FILTER_ID = 11,
+       RADIUS_ATTR_FRAMED_COMPRESSION = 13,
+       RADIUS_ATTR_LOGIN_IP_HOST = 14,
+       RADIUS_ATTR_LOGIN_SERVICE = 15,
+       RADIUS_ATTR_LOGIN_TCP_PORT = 16,
+       RADIUS_ATTR_CALLBACK_NUMBER = 19,
+       RADIUS_ATTR_CALLBACK_ID = 20,
+       RADIUS_ATTR_FRAMED_ROUTE = 22,
+       RADIUS_ATTR_FRAMED_IPX_NETWORK = 23,
+       RADIUS_ATTR_LOGIN_LAT_SERVICE = 34,
+       RADIUS_ATTR_LOGIN_LAT_NODE = 35,
+       RADIUS_ATTR_LOGIN_LAT_GROUP = 36,
+       RADIUS_ATTR_FRAMED_APPLETALK_LINK = 37,
+       RADIUS_ATTR_FRAMED_APPLETALK_NETWORK = 38,
+       RADIUS_ATTR_FRAMED_APPLETALK_ZONE = 39,
+       RADIUS_ATTR_CHAP_CHALLENGE = 60,
+       RADIUS_ATTR_PORT_LIMIT = 62,
+       RADIUS_ATTR_LOGIN_LAT_PORT = 63,
+       RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT = 66,
+       RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT = 67,
+       RADIUS_ATTR_TUNNEL_PASSWORD = 69,
+       RADIUS_ATTR_ARAP_PASSWORD = 70,
+       RADIUS_ATTR_ARAP_FEATURES = 71,
+       RADIUS_ATTR_ARAP_ZONE_ACCESS = 72,
+       RADIUS_ATTR_ARAP_SECURITY = 73,
+       RADIUS_ATTR_ARAP_SECURITY_DATA = 74,
+       RADIUS_ATTR_PASSWORD_RETRY = 75,
+       RADIUS_ATTR_PROMPT = 76,
+       RADIUS_ATTR_CONFIGURATION_TOKEN = 78,
+       RADIUS_ATTR_TUNNEL_ASSIGNEMENT_ID = 82,
+       RADIUS_ATTR_TUNNEL_PREFERENCE = 83,
+       RADIUS_ATTR_ARAP_CHALLENGE_RESPONSE = 84,
+       RADIUS_ATTR_NAS_PORT_ID = 87,
+       RADIUS_ATTR_FRAMED_POOL = 88,
+       RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID = 90,
+       RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID = 91,
+       RADIUS_ATTR_ORIGINATING_LINE_INFO = 94,
+       RADIUS_ATTR_FRAMED_INTERFACE_ID = 96,
+       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_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_ROUTE_RECORD = 282,
+	DIAM_ATTR_PROXY_INFO = 284,
+	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
+};
+
+#endif /* _RGW_COMMON_H */
+  
Index: extensions/app_radgw/rgw_conf.l
===================================================================
--- extensions/app_radgw/rgw_conf.l	(revision 254)
+++ extensions/app_radgw/rgw_conf.l	(revision 254)
@@ -0,0 +1,276 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+/* Lex configuration parser for radius_gw extension.
+ *
+ */
+
+%{
+#include "rgw.h"
+#include "rgw_conf.tab.h"
+
+/* Update the column information */
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+}
+
+/* %option noinput ? */
+#define YY_NO_INPUT
+%}
+
+%option bison-bridge bison-locations
+%option noyywrap
+%option nounput
+
+/* Use the following start condition to parse an URI */
+%x	IN_PLG
+%x	IN_CLI1
+%x	IN_CLI2
+%x	EXPECT_IP4
+%x	EXPECT_IP6
+%x	EXPECT_DECINT
+
+/* Quoted string. Multilines do not match. */
+qstring		\"[^\"\n]*\"
+
+/* Used to match IP, IP6, and port */
+IP4		[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
+IP6		[[:xdigit:]]*:[[:xdigit:]]*:[[:xdigit:].:]*
+BR_PORT		[[][0-9]+[]]
+
+
+%%
+
+	/* All sections */
+<*>\n			{ 
+				/* Update the line count */
+				yylloc->first_line++; 
+				yylloc->last_line++; 
+				yylloc->last_column=0; 
+			} 
+
+<*>([[:space:]]{-}[\n])+	; /* Eat all spaces, not new lines */
+<*>#.*$			; /* Eat all comments */
+
+<*>{qstring}		{
+				/* First copy the string without the quotes for use in the yacc parser */
+				yylval->string = strdup(yytext+1);
+				if (yylval->string == NULL) {
+					fd_log_debug("Unable to allocate memory: %s\n", strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+				}
+
+				yylval->string[yyleng-2] = '\0';
+				
+				/* the yacc parser will check the string is valid */
+				return QSTRING;
+			}
+
+
+	/* Extension section */			
+(?i:"RGWX")		{ BEGIN(IN_PLG); return PLG_PREFIX; 		}
+
+<IN_PLG>(?i:"auth")	{ return AUTH; }
+<IN_PLG>(?i:"acct")	{ return ACCT; }
+
+<IN_PLG,IN_CLI2>[[:xdigit:]]+	{
+				/* Convert this to an integer value */
+				int ret = sscanf(yytext, "%x", &yylval->integer);
+				if (ret != 1) {
+					/* No matching: an error occurred */
+					fd_log_debug("Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+					/* Maybe we could REJECT instead of failing here? */
+				}
+				return INTEGER;
+			}
+
+<IN_PLG>[:]		{ return yytext[0]; }
+
+
+	/* Client section */
+(?i:"cli")		{ BEGIN(IN_CLI1); return CLI_PREFIX; 		}
+
+	/* Match an IP (4 or 6) and optional port */
+<IN_CLI1>({IP4}|{IP6}){BR_PORT}?	{
+				char * work;
+				char * port;
+				unsigned short p = 0;
+				
+				work = strdup(yytext);
+				if ( work == NULL ) {
+					fd_log_debug("Unable to allocate memory: %s\n", strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+				}
+				
+				if (port = strchr(work, '[')) {
+					*port = '\0';
+					port++;
+					if (sscanf(port, "%hu]", &p) != 1) {
+						fd_log_debug("'%s' is not a valid port: %s\n", port, strerror(errno));
+						free(work);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+				}
+				
+				/* Do we have an IP or IPv6? Let's check if we have ':' char somewhere in the beginning */
+				if (memchr(work, ':', 5) != NULL) {
+					struct sockaddr_in6 * sin6 = NULL;
+				
+					sin6 = malloc(sizeof(struct sockaddr_in6));
+					if (sin6 == NULL) {
+						fd_log_debug("Unable to allocate memory: %s\n", strerror(errno));
+						free(work);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					memset(sin6, 0, sizeof(struct sockaddr_in6));
+					sin6->sin6_family = AF_INET6;
+					if (inet_pton(AF_INET6, work, &sin6->sin6_addr) != 1) {
+						fd_log_debug("'%s' is not a valid IPv6 address: %s\n", work, strerror(errno));
+						free(work);
+						free(sin6);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					sin6->sin6_port = htons(p);
+					yylval->ss = (struct sockaddr *)sin6;
+				} else {
+					struct sockaddr_in * sin = NULL;
+				
+					sin = malloc(sizeof(struct sockaddr_in));
+					if (sin == NULL) {
+						fd_log_debug("Unable to allocate memory: %s\n", strerror(errno));
+						free(work);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					memset(sin, 0, sizeof(struct sockaddr_in));
+					sin->sin_family = AF_INET;
+					if (inet_pton(AF_INET, work, &sin->sin_addr) != 1) {
+						fd_log_debug("'%s' is not a valid IP address: %s\n", work, strerror(errno));
+						free(work);
+						free(sin);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					sin->sin_port = htons(p);
+					yylval->ss = (struct sockaddr *)sin;
+				}
+				free(work);
+				return IP;
+			}
+
+
+<IN_CLI1>"/"		{ BEGIN(IN_CLI2); return '/';	 		}
+
+
+	/* Servers section */
+(?i:"auth_server_enable")	{ return AUTH_ENABLE; 				}
+(?i:"auth_server_port")		{ BEGIN(EXPECT_DECINT); return AUTH_PORT; 	}
+(?i:"auth_server_ip4")		{ BEGIN(EXPECT_IP4); return AUTH_IP4; 		}
+(?i:"auth_server_ip6")		{ BEGIN(EXPECT_IP6); return AUTH_IP6; 		}
+(?i:"acct_server_enable")	{ return ACCT_ENABLE; 				}
+(?i:"acct_server_port")		{ BEGIN(EXPECT_DECINT); return ACCT_PORT; 	}
+(?i:"acct_server_ip4")		{ BEGIN(EXPECT_IP4); return ACCT_IP4; 		}
+(?i:"acct_server_ip6")		{ BEGIN(EXPECT_IP6); return ACCT_IP6; 		}
+
+<EXPECT_DECINT>[[:digit:]]+	{
+					/* Match an integer (not hexa) */
+					int ret = sscanf(yytext, "%d", &yylval->integer);
+					if (ret != 1) {
+						/* No matching: an error occurred */
+						fd_log_debug("Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno));
+						return LEX_ERROR; /* trig an error in yacc parser */
+						/* Maybe we could REJECT instead of failing here? */
+					}
+					return INTEGER;
+				}
+
+<EXPECT_IP4,EXPECT_IP6>(?i:"disable")	{ return DISABLED; 				}
+				
+<EXPECT_IP4>{IP4}		{
+					struct sockaddr_in * sin = NULL;
+				
+					sin = malloc(sizeof(struct sockaddr_in));
+					if (sin == NULL) {
+						fd_log_debug("Unable to allocate memory: %s\n", strerror(errno));
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					memset(sin, 0, sizeof(struct sockaddr_in));
+					sin->sin_family = AF_INET;
+					if (inet_pton(AF_INET, yytext, &sin->sin_addr) != 1) {
+						fd_log_debug("'%s' is not a valid IP address: %s\n", yytext, strerror(errno));
+						free(sin);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					yylval->ss = (struct sockaddr *)sin;
+					return IP;
+				}
+
+<EXPECT_IP6>{IP6}		{
+					struct sockaddr_in6 * sin6 = NULL;
+				
+					sin6 = malloc(sizeof(struct sockaddr_in6));
+					if (sin6 == NULL) {
+						fd_log_debug("Unable to allocate memory: %s\n", strerror(errno));
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					memset(sin6, 0, sizeof(struct sockaddr_in6));
+					sin6->sin6_family = AF_INET6;
+					if (inet_pton(AF_INET6, yytext, &sin6->sin6_addr) != 1) {
+						fd_log_debug("'%s' is not a valid IPv6 address: %s\n", yytext, strerror(errno));
+						free(sin6);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					yylval->ss = (struct sockaddr *)sin6;
+					return IP;
+				}
+
+	
+	/* Valid single characters for yyparse in all contexts */
+<*>[=]			{ return yytext[0]; }
+<*>[;]			{ BEGIN(INITIAL); return yytext[0]; }
+
+	/* Unrecognized token */
+<*>[[:alnum:]]+		|	/* This rule is only useful to print a complete token in error messages */
+	/* Unrecognized character */
+<*>.			{ 
+				fd_log_debug("Unrecognized text on line %d col %d: '%s'.\n", yylloc->first_line, yylloc->first_column, yytext);
+			 	return LEX_ERROR; 
+			}
+
+%%
Index: extensions/app_radgw/rgw_conf.y
===================================================================
--- extensions/app_radgw/rgw_conf.y	(revision 254)
+++ extensions/app_radgw/rgw_conf.y	(revision 254)
@@ -0,0 +1,404 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Yacc extension's configuration parser.
+ * See doc/app_radgw.conf.sample for configuration file format
+ */
+
+/* For development only : */
+%debug 
+%error-verbose
+
+/* The parser receives the configuration file filename as parameter */
+%parse-param {char * conffile}
+
+/* Keep track of location */
+%locations 
+%pure-parser
+
+%{
+#include "rgw.h"
+#include "rgw_conf.tab.h"	/* bison is not smart enough to define the YYLTYPE before including this code, so... */
+
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+/* Forward declaration */
+int yyparse(char * conffile);
+
+/* Parse the configuration file */
+int rgw_conf_handle(char * conffile)
+{
+	extern FILE * rgw_confin;
+	int ret;
+	
+	rgw_confin = fopen(conffile, "r");
+	if (rgw_confin == NULL) {
+		ret = errno;
+		fd_log_debug("Unable to open extension configuration file %s for reading: %s\n", conffile, strerror(ret));
+		return ret;
+	}
+
+	ret = rgw_confparse(conffile);
+
+	fclose(rgw_confin);
+
+	if (ret != 0) {
+		return EINVAL;
+	}
+	
+	return 0;
+}
+
+/* The Lex parser prototype */
+int rgw_conflex(YYSTYPE *lvalp, YYLTYPE *llocp);
+
+/* Function to report the errors */
+void yyerror (YYLTYPE *ploc, char * conffile, char const *s)
+{
+	if (ploc->first_line != ploc->last_line)
+		fd_log_debug("%s:%d.%d-%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
+	else if (ploc->first_column != ploc->last_column)
+		fd_log_debug("%s:%d.%d-%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
+	else
+		fd_log_debug("%s:%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, s);
+}
+
+/* This function checks a string value is a valid filename */
+static int is_valid_file( char * candidate ) 
+{
+	int ret;
+	struct stat buffer;
+	
+	ret = stat(candidate, &buffer);
+	if (ret != 0) {
+		fd_log_debug("Error on file '%s': %s.\n", candidate, strerror(errno));
+		return 0;
+	}
+
+	/* Ok this candidate is valid */
+	return 1;
+}
+
+/* Very simple byte stack management */
+static unsigned char * buf = NULL;
+static size_t buf_sz = 0;
+static size_t buf_rsz = 0;
+static inline int buf_add(unsigned char val) /* add a value in the array */
+{
+	buf_sz++;
+	
+	if (buf_sz > buf_rsz) {
+		void * rez=NULL;
+		buf_rsz += 256;
+		rez = realloc(buf, buf_rsz);
+		if (rez == NULL) {
+			fd_log_debug("Error on memory allocation: %s\n", strerror(errno));
+			return 0;
+		}
+		buf = (unsigned char *)rez;
+	}
+	buf[buf_sz - 1] = val;
+	return 1;   
+}
+static inline void buf_reinit(void)
+{
+	free(buf);
+	buf = NULL;
+	buf_sz = 0;
+	buf_rsz = 0;
+}
+
+static int port = 0;
+static char * plgconffile = NULL;
+
+%}
+
+/* Values returned by lex for token */
+%union {
+	char 		*string;	/* The string is allocated by strdup in lex.*/
+	int		 integer;	/* Store integer values */
+	struct sockaddr *ss;		/* sockaddr to free after use (alloc in lex) */
+}
+
+/* typed data */
+%token <string>	QSTRING
+%token <integer> INTEGER
+%token <ss>	IP
+
+%type <string>	FILENAME
+
+/* simple tokens */
+%token		DISABLED
+%token		AUTH
+%token		ACCT
+
+%token 		PLG_PREFIX
+%token 		CLI_PREFIX
+
+%token		AUTH_ENABLE
+%token		AUTH_PORT
+%token		AUTH_IP4
+%token		AUTH_IP6
+%token		ACCT_ENABLE
+%token		ACCT_PORT
+%token		ACCT_IP4
+%token		ACCT_IP6
+
+/* In case of error in the lexical analysis */
+%token 		LEX_ERROR
+
+
+/* -------------------------------------- */
+%%
+
+	/* The grammar definition */
+conffile:		/* empty grammar is OK */
+			| conffile plugin
+			| conffile clientdef
+			| conffile authserv
+			| conffile acctserv
+			;
+
+				
+/* -------------------------------------- */
+FILENAME:		QSTRING
+			{
+				/* Verify this is a valid file */
+				if (!is_valid_file($1)) {
+					yyerror (&yylloc, conffile, "Error on file name, aborting...");
+					YYERROR;
+				}
+				$$ = $1;
+			}
+			;
+/* -------------------------------------- */
+plugin:			{
+				/* Reset the parameters */
+				buf_reinit();
+				port = RGW_PLG_TYPE_AUTH | RGW_PLG_TYPE_ACCT ;
+				free(plgconffile); plgconffile = NULL;
+			}
+			PLG_PREFIX '=' FILENAME plg_attributes ';'
+			{
+				/* Add this extension in the list */
+				if ( rgw_plg_add( $4, plgconffile, port, &buf, buf_sz ) ) {
+					yyerror (&yylloc, conffile, "Error parsing / adding extension !");
+					YYERROR;
+				}
+				
+				/* Free the array */
+				buf_reinit();
+				
+				/* stop conffile from being freed here */
+				plgconffile = NULL;
+			}
+			;
+
+plg_attributes:		/* empty */
+			| plg_attributes ':' QSTRING
+			{
+				plgconffile = $3;
+			}
+			| plg_attributes ':' AUTH
+			{
+				port = RGW_PLG_TYPE_AUTH;
+			}
+			| plg_attributes ':' ACCT
+			{
+				port = RGW_PLG_TYPE_ACCT;
+			}
+			| plg_attributes ':' extcodes_list
+			;
+
+extcodes_list:		/* empty */
+			| extcodes_list INTEGER
+			{
+				if ($2 < 0 || $2 > 255) {
+					yyerror (&yylloc, conffile, "Invalid command code value!");
+					YYERROR;
+				}
+				if ( ! buf_add((unsigned char)$2) ) {
+					yyerror (&yylloc, conffile, "Error allocating memory!");
+					YYERROR;
+				}
+			}
+			;
+				
+/* -------------------------------------- */
+
+clientdef:		{
+				buf_reinit();
+			}
+			CLI_PREFIX '=' IP '/' clisecret_key ';'
+			{
+				/* Add this client */
+				if ( rgw_clients_add( $4, &buf, buf_sz ) ) {
+					yyerror (&yylloc, conffile, "Error parsing / adding client !");
+					YYERROR;
+				}
+				
+				/* reinit the buffer */
+				buf_reinit();
+			}
+			;
+
+clisecret_key:		/* empty */
+			| clisecret_key QSTRING
+			{
+				int i;
+				size_t len = strlen($2);
+				for (i = 0; i < len; i++) {
+					if ( ! buf_add( $2 [i] ) ) {
+						yyerror (&yylloc, conffile, "Memory allocation error.");
+						YYERROR;
+					}
+				}
+				
+				free($2);
+			}
+			| clisecret_key INTEGER
+			{
+				if ( $2 < 0 || $2 > 255 ) {
+					yyerror (&yylloc, conffile, "Invalid value in key.");
+					YYERROR;
+				}
+				
+				if ( ! buf_add( $2 ) ) {
+					yyerror (&yylloc, conffile, "Memory allocation error.");
+					YYERROR;
+				}
+			}
+			;
+
+/* -------------------------------------- */
+
+authserv:		AUTH_ENABLE '=' INTEGER ';'
+			{
+				if ($3 == 0) {
+					rgw_servers.auth_serv.disabled = 1;
+				} else {
+					rgw_servers.auth_serv.disabled = 0;
+				}
+			}
+			| AUTH_PORT '=' INTEGER ';'
+			{
+				if ($3 <= 0 || $3 > 65535) {
+					yyerror (&yylloc, conffile, "Invalid port number !");
+					YYERROR;
+				}
+					
+				rgw_servers.auth_serv.port = htons($3);
+			}
+			| AUTH_IP4 '=' DISABLED ';'
+			{
+				rgw_servers.auth_serv.ip_disabled = 1;
+			}
+			| AUTH_IP4 '=' IP ';'
+			{
+				if (((struct sockaddr *)($3))->sa_family != AF_INET) {
+					yyerror (&yylloc, conffile, "Invalid address specification !");
+					YYERROR;
+				}
+				memcpy( & rgw_servers.auth_serv.ip_endpoint, &((struct sockaddr_in *)($3))->sin_addr, sizeof(struct in_addr) );
+				free($3);
+				rgw_servers.auth_serv.ip_disabled = 0;
+			}
+			| AUTH_IP6 '=' DISABLED ';'
+			{
+				rgw_servers.auth_serv.ip6_disabled = 1;
+			}
+			| AUTH_IP6 '=' IP ';'
+			{
+				if (((struct sockaddr *)($3)) -> sa_family != AF_INET6) {
+					yyerror (&yylloc, conffile, "Invalid address specification !");
+					YYERROR;
+				}
+				memcpy( & rgw_servers.auth_serv.ip6_endpoint, &((struct sockaddr_in6 *)($3))->sin6_addr, sizeof(struct in6_addr) );
+				free($3);
+				rgw_servers.auth_serv.ip6_disabled = 0;
+			}
+			;
+
+/* -------------------------------------- */
+
+acctserv:		ACCT_ENABLE '=' INTEGER ';'
+			{
+				if ($3 == 0) {
+					rgw_servers.acct_serv.disabled = 1;
+				} else {
+					rgw_servers.acct_serv.disabled = 0;
+				}
+			}
+			| ACCT_PORT '=' INTEGER ';'
+			{
+				if ($3 <= 0 || $3 > 65535) {
+					yyerror (&yylloc, conffile, "Invalid port number !");
+					YYERROR;
+				}
+					
+				rgw_servers.acct_serv.port = htons($3);
+			}
+			| ACCT_IP4 '=' DISABLED ';'
+			{
+				rgw_servers.acct_serv.ip_disabled = 1;
+			}
+			| ACCT_IP4 '=' IP ';'
+			{
+				if (((struct sockaddr *)($3)) -> sa_family != AF_INET) {
+					yyerror (&yylloc, conffile, "Invalid address specification !");
+					YYERROR;
+				}
+				memcpy( & rgw_servers.auth_serv.ip_endpoint, &((struct sockaddr_in *)($3))->sin_addr, sizeof(struct in_addr) );
+				free($3);
+				rgw_servers.acct_serv.ip_disabled = 0;
+			}
+			| ACCT_IP6 '=' DISABLED ';'
+			{
+				rgw_servers.acct_serv.ip6_disabled = 1;
+			}
+			| ACCT_IP6 '=' IP ';'
+			{
+				if (((struct sockaddr *)($3)) -> sa_family != AF_INET6) {
+					yyerror (&yylloc, conffile, "Invalid address specification !");
+					YYERROR;
+				}
+				memcpy( & rgw_servers.auth_serv.ip6_endpoint, &((struct sockaddr_in6 *)($3))->sin6_addr, sizeof(struct in6_addr) );
+				free($3);
+				rgw_servers.acct_serv.ip6_disabled = 0;
+			}
+			;
Index: extensions/app_radgw/rgw_main.c
===================================================================
--- extensions/app_radgw/rgw_main.c	(revision 254)
+++ extensions/app_radgw/rgw_main.c	(revision 254)
@@ -0,0 +1,76 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+/* 
+ * Main file of the app_radgw extension.
+ */
+
+#include "rgw.h"
+
+/* Extension entry point called by freeDiameter */
+static int rgw_main(char * conffile) 
+{
+	CHECK_FCT( rgw_msg_init() );
+	
+	CHECK_FCT( rgw_servers_init() );
+	
+	CHECK_FCT( rgw_conf_handle(conffile) );
+	
+	TRACE_DEBUG(INFO, "Extension RADIUS Gateway initialized with configuration: '%s'", conffile);
+	rgw_servers_dump();
+	rgw_clients_dump();
+	rgw_plg_dump();
+	
+	/* Start making extension list accelerators */
+	rgw_plg_start_cache();
+	
+	/* Start the worker threads */
+	CHECK_FCT( rgw_work_start() );
+	
+	/* Start the servers */
+	CHECK_FCT( rgw_servers_start() );
+	
+	return 0;
+}
+
+/* Unload */
+void fd_ext_fini(void)
+{
+	rgw_servers_fini();
+	rgw_work_fini();
+	rgw_plg_fini();
+	rgw_clients_fini();
+}
+
+EXTENSION_ENTRY("app_radgw", rgw_main);
Index: extensions/app_radgw/rgw_msg.c
===================================================================
--- extensions/app_radgw/rgw_msg.c	(revision 254)
+++ extensions/app_radgw/rgw_msg.c	(revision 254)
@@ -0,0 +1,345 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* This file contains all support functions to parse, create, and manipulate RADIUS messages. Other 
+modules do not need to "know" the actual representation of RADIUS messages on the network. They only 
+receive the logical view as exposed in the rgw.h file. This file extends the content of the radius.c 
+file functions (from hostap project).*/
+
+#include "rgw.h"
+
+/* Directly include the code of the functions for dumping (generated from the IANA registries) */
+#include "rgw_msg_codes.c"
+#include "rgw_msg_attrtype.c"
+
+/* Destroy a message */
+void rgw_msg_free(struct rgw_radius_msg_meta ** msg)
+{
+	if (!msg || !*msg)
+		return;
+	
+	radius_msg_free(&(*msg)->radius);
+	free(*msg);
+	*msg = NULL;
+}
+
+/* This function creates a rgw_radius_msg_meta structure after parsing a RADIUS buffer */
+int rgw_msg_parse(unsigned char * buf, size_t len, struct rgw_radius_msg_meta ** msg)
+{
+	struct radius_msg * temp_msg = NULL;
+	
+	TRACE_ENTRY("%p %g %p", buf, len, msg);
+	
+	CHECK_PARAMS( buf && len && msg );
+	
+	*msg = NULL;
+	
+	/* Parse the RADIUS message */
+	temp_msg = radius_msg_parse(buf, len);
+	if (temp_msg == NULL) {
+		TRACE_DEBUG(INFO, "Error parsing the RADIUS message, discarding");
+		return EINVAL;
+	}
+	
+	/* Now alloc space for the meta-data */
+	CHECK_MALLOC( *msg = realloc(temp_msg, sizeof(struct rgw_radius_msg_meta)) );
+	
+	/* Clear memory after the parsed data */
+	memset( &(*msg)->radius + 1, 0, sizeof(struct rgw_radius_msg_meta) - sizeof(struct radius_msg) );
+	
+	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)
+{
+	unsigned char *auth;
+	size_t i;
+	if (! TRACE_BOOL(FULL) )
+		return;
+	
+	auth =  &(msg->radius.hdr->authenticator[0]);
+	
+	fd_log_debug("------ RADIUS msg dump -------\n");
+	fd_log_debug(" id  : 0x%02hhx, code : %hhd (%s)\n", msg->radius.hdr->identifier, msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
+	fd_log_debug(" auth: %02hhx %02hhx %02hhx %02hhx  %02hhx %02hhx %02hhx %02hhx\n",
+			auth[0], auth[1], auth[2], auth[3], 
+			auth[4], auth[5], auth[6], auth[7]);
+	fd_log_debug("       %02hhx %02hhx %02hhx %02hhx  %02hhx %02hhx %02hhx %02hhx\n",
+			auth[8],  auth[9],  auth[10], auth[11], 
+			auth[12], auth[13], auth[14], auth[15]);
+	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... */
+	}
+	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)
+{
+	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;
+		}
+		
+	}
+	
+	/* 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) );
+		TRACE_DEBUG(FULL, "Session '%s' has been successfully %s.", sess_str, idx ? "created" : "retrieved");
+		
+		/* 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));
+	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;
+}
Index: extensions/app_radgw/rgw_msg_attrtype.c
===================================================================
--- extensions/app_radgw/rgw_msg_attrtype.c	(revision 254)
+++ extensions/app_radgw/rgw_msg_attrtype.c	(revision 254)
@@ -0,0 +1,262 @@
+/*  Name of RADIUS attribute from its code */
+const char * rgw_msg_attrtype_str(unsigned char c) {
+		/* 1         User-Name                                 */
+	if ( c == 1) return "User-Name ";
+		/* 2         User-Password                             */
+	if ( c == 2) return "User-Password ";
+		/* 3         CHAP-Password                             */
+	if ( c == 3) return "CHAP-Password ";
+		/* 4         NAS-IP-Address                            */
+	if ( c == 4) return "NAS-IP-Address ";
+		/* 5         NAS-Port                                  */
+	if ( c == 5) return "NAS-Port ";
+		/* 6         Service-Type                              */
+	if ( c == 6) return "Service-Type ";
+		/* 7         Framed-Protocol                           */
+	if ( c == 7) return "Framed-Protocol ";
+		/* 8         Framed-IP-Address                         */
+	if ( c == 8) return "Framed-IP-Address ";
+		/* 9         Framed-IP-Netmask                         */
+	if ( c == 9) return "Framed-IP-Netmask ";
+		/* 10        Framed-Routing                            */
+	if ( c == 10) return "Framed-Routing ";
+		/* 11        Filter-Id                                 */
+	if ( c == 11) return "Filter-Id ";
+		/* 12        Framed-MTU                                */
+	if ( c == 12) return "Framed-MTU ";
+		/* 13        Framed-Compression                        */
+	if ( c == 13) return "Framed-Compression ";
+		/* 14        Login-IP-Host                             */
+	if ( c == 14) return "Login-IP-Host ";
+		/* 15        Login-Service                             */
+	if ( c == 15) return "Login-Service ";
+		/* 16        Login-TCP-Port                            */
+	if ( c == 16) return "Login-TCP-Port ";
+		/* 17        Unassigned */
+	if ( c == 17) return "Unassigned ";
+		/* 18        Reply-Message                             */
+	if ( c == 18) return "Reply-Message ";
+		/* 19        Callback-Number                           */
+	if ( c == 19) return "Callback-Number ";
+		/* 20        Callback-Id                               */
+	if ( c == 20) return "Callback-Id ";
+		/* 21        Unassigned */
+	if ( c == 21) return "Unassigned ";
+		/* 22        Framed-Route                              */
+	if ( c == 22) return "Framed-Route ";
+		/* 23        Framed-IPX-Network                        */
+	if ( c == 23) return "Framed-IPX-Network ";
+		/* 24        State                                     */
+	if ( c == 24) return "State ";
+		/* 25        Class                                     */
+	if ( c == 25) return "Class ";
+		/* 26        Vendor-Specific                           */
+	if ( c == 26) return "Vendor-Specific ";
+		/* 27        Session-Timeout                           */
+	if ( c == 27) return "Session-Timeout ";
+		/* 28        Idle-Timeout                              */
+	if ( c == 28) return "Idle-Timeout ";
+		/* 29        Termination-Action                        */
+	if ( c == 29) return "Termination-Action ";
+		/* 30        Called-Station-Id                         */
+	if ( c == 30) return "Called-Station-Id ";
+		/* 31        Calling-Station-Id                        */
+	if ( c == 31) return "Calling-Station-Id ";
+		/* 32        NAS-Identifier                            */
+	if ( c == 32) return "NAS-Identifier ";
+		/* 33        Proxy-State                               */
+	if ( c == 33) return "Proxy-State ";
+		/* 34        Login-LAT-Service                         */
+	if ( c == 34) return "Login-LAT-Service ";
+		/* 35        Login-LAT-Node                            */
+	if ( c == 35) return "Login-LAT-Node ";
+		/* 36        Login-LAT-Group                           */
+	if ( c == 36) return "Login-LAT-Group ";
+		/* 37        Framed-AppleTalk-Link                     */
+	if ( c == 37) return "Framed-AppleTalk-Link ";
+		/* 38        Framed-AppleTalk-Network                  */
+	if ( c == 38) return "Framed-AppleTalk-Network ";
+		/* 39        Framed-AppleTalk-Zone                     */
+	if ( c == 39) return "Framed-AppleTalk-Zone ";
+		/* 40        Acct-Status-Type                         [RFC2866] */
+	if ( c == 40) return "Acct-Status-Type [RFC2866]";
+		/* 41        Acct-Delay-Time                          [RFC2866] */
+	if ( c == 41) return "Acct-Delay-Time [RFC2866]";
+		/* 42        Acct-Input-Octets                        [RFC2866] */
+	if ( c == 42) return "Acct-Input-Octets [RFC2866]";
+		/* 43        Acct-Output-Octets                       [RFC2866] */
+	if ( c == 43) return "Acct-Output-Octets [RFC2866]";
+		/* 44        Acct-Session-Id                          [RFC2866] */
+	if ( c == 44) return "Acct-Session-Id [RFC2866]";
+		/* 45        Acct-Authentic                           [RFC2866] */
+	if ( c == 45) return "Acct-Authentic [RFC2866]";
+		/* 46        Acct-Session-Time                        [RFC2866] */
+	if ( c == 46) return "Acct-Session-Time [RFC2866]";
+		/* 47        Acct-Input-Packets                       [RFC2866] */
+	if ( c == 47) return "Acct-Input-Packets [RFC2866]";
+		/* 48        Acct-Output-Packets                      [RFC2866] */
+	if ( c == 48) return "Acct-Output-Packets [RFC2866]";
+		/* 49        Acct-Terminate-Cause                     [RFC2866] */
+	if ( c == 49) return "Acct-Terminate-Cause [RFC2866]";
+		/* 50        Acct-Multi-Session-Id                    [RFC2866] */
+	if ( c == 50) return "Acct-Multi-Session-Id [RFC2866]";
+		/* 51        Acct-Link-Count                          [RFC2866] */
+	if ( c == 51) return "Acct-Link-Count [RFC2866]";
+		/* 52        Acct-Input-Gigawords                     [RFC2869] */
+	if ( c == 52) return "Acct-Input-Gigawords [RFC2869]";
+		/* 53        Acct-Output-Gigawords                    [RFC2869] */
+	if ( c == 53) return "Acct-Output-Gigawords [RFC2869]";
+		/* 54        Unassigned */
+	if ( c == 54) return "Unassigned ";
+		/* 55        Event-Timestamp                          [RFC2869] */
+	if ( c == 55) return "Event-Timestamp [RFC2869]";
+		/* 56        Egress-VLANID                            [RFC4675] */
+	if ( c == 56) return "Egress-VLANID [RFC4675]";
+		/* 57        Ingress-Filters                          [RFC4675] */
+	if ( c == 57) return "Ingress-Filters [RFC4675]";
+		/* 58        Egress-VLAN-Name                         [RFC4675] */
+	if ( c == 58) return "Egress-VLAN-Name [RFC4675]";
+		/* 59        User-Priority-Table                      [RFC4675] */
+	if ( c == 59) return "User-Priority-Table [RFC4675]";
+		/* 60        CHAP-Challenge */
+	if ( c == 60) return "CHAP-Challenge ";
+		/* 61        NAS-Port-Type */
+	if ( c == 61) return "NAS-Port-Type ";
+		/* 62        Port-Limit */
+	if ( c == 62) return "Port-Limit ";
+		/* 63        Login-LAT-Port */
+	if ( c == 63) return "Login-LAT-Port ";
+		/* 64        Tunnel-Type                              [RFC2868] */
+	if ( c == 64) return "Tunnel-Type [RFC2868]";
+		/* 65        Tunnel-Medium-Type                       [RFC2868] */
+	if ( c == 65) return "Tunnel-Medium-Type [RFC2868]";
+		/* 66        Tunnel-Client-Endpoint                   [RFC2868] */
+	if ( c == 66) return "Tunnel-Client-Endpoint [RFC2868]";
+		/* 67        Tunnel-Server-Endpoint                   [RFC2868] */
+	if ( c == 67) return "Tunnel-Server-Endpoint [RFC2868]";
+		/* 68        Acct-Tunnel-Connection                   [RFC2867] */
+	if ( c == 68) return "Acct-Tunnel-Connection [RFC2867]";
+		/* 69        Tunnel-Password                          [RFC2868] */
+	if ( c == 69) return "Tunnel-Password [RFC2868]";
+		/* 70        ARAP-Password                            [RFC2869] */
+	if ( c == 70) return "ARAP-Password [RFC2869]";
+		/* 71        ARAP-Features                            [RFC2869] */
+	if ( c == 71) return "ARAP-Features [RFC2869]";
+		/* 72        ARAP-Zone-Access                         [RFC2869] */
+	if ( c == 72) return "ARAP-Zone-Access [RFC2869]";
+		/* 73        ARAP-Security                            [RFC2869] */
+	if ( c == 73) return "ARAP-Security [RFC2869]";
+		/* 74        ARAP-Security-Data                       [RFC2869] */
+	if ( c == 74) return "ARAP-Security-Data [RFC2869]";
+		/* 75        Password-Retry                           [RFC2869] */
+	if ( c == 75) return "Password-Retry [RFC2869]";
+		/* 76        Prompt                                   [RFC2869] */
+	if ( c == 76) return "Prompt [RFC2869]";
+		/* 77        Connect-Info                             [RFC2869] */
+	if ( c == 77) return "Connect-Info [RFC2869]";
+		/* 78        Configuration-Token                      [RFC2869] */
+	if ( c == 78) return "Configuration-Token [RFC2869]";
+		/* 79        EAP-Message                              [RFC2869] */
+	if ( c == 79) return "EAP-Message [RFC2869]";
+		/* 80        Message-Authenticator                    [RFC2869] */
+	if ( c == 80) return "Message-Authenticator [RFC2869]";
+		/* 81        Tunnel-Private-Group-ID                  [RFC2868] */
+	if ( c == 81) return "Tunnel-Private-Group-ID [RFC2868]";
+		/* 82        Tunnel-Assignment-ID                     [RFC2868] */
+	if ( c == 82) return "Tunnel-Assignment-ID [RFC2868]";
+		/* 83        Tunnel-Preference                        [RFC2868] */
+	if ( c == 83) return "Tunnel-Preference [RFC2868]";
+		/* 84        ARAP-Challenge-Response                  [RFC2869] */
+	if ( c == 84) return "ARAP-Challenge-Response [RFC2869]";
+		/* 85        Acct-Interim-Interval                    [RFC2869] */
+	if ( c == 85) return "Acct-Interim-Interval [RFC2869]";
+		/* 86        Acct-Tunnel-Packets-Lost                 [RFC2867] */
+	if ( c == 86) return "Acct-Tunnel-Packets-Lost [RFC2867]";
+		/* 87        NAS-Port-Id                              [RFC2869] */
+	if ( c == 87) return "NAS-Port-Id [RFC2869]";
+		/* 88        Framed-Pool                              [RFC2869] */
+	if ( c == 88) return "Framed-Pool [RFC2869]";
+		/* 89        CUI                                      [RFC4372] */
+	if ( c == 89) return "CUI [RFC4372]";
+		/* 90        Tunnel-Client-Auth-ID                    [RFC2868] */
+	if ( c == 90) return "Tunnel-Client-Auth-ID [RFC2868]";
+		/* 91        Tunnel-Server-Auth-ID                    [RFC2868] */
+	if ( c == 91) return "Tunnel-Server-Auth-ID [RFC2868]";
+		/* 92        NAS-Filter-Rule                          [RFC4849] */
+	if ( c == 92) return "NAS-Filter-Rule [RFC4849]";
+		/* 93        Unassigned */
+	if ( c == 93) return "Unassigned ";
+		/* 94        Originating-Line-Info                    [RFC4005] */
+	if ( c == 94) return "Originating-Line-Info [RFC4005]";
+		/* 95        NAS-IPv6-Address                         [RFC3162] */
+	if ( c == 95) return "NAS-IPv6-Address [RFC3162]";
+		/* 96        Framed-Interface-Id                      [RFC3162] */
+	if ( c == 96) return "Framed-Interface-Id [RFC3162]";
+		/* 97        Framed-IPv6-Prefix                       [RFC3162] */
+	if ( c == 97) return "Framed-IPv6-Prefix [RFC3162]";
+		/* 98        Login-IPv6-Host                          [RFC3162] */
+	if ( c == 98) return "Login-IPv6-Host [RFC3162]";
+		/* 99        Framed-IPv6-Route                        [RFC3162] */
+	if ( c == 99) return "Framed-IPv6-Route [RFC3162]";
+		/* 100       Framed-IPv6-Pool                         [RFC3162] */
+	if ( c == 100) return "Framed-IPv6-Pool [RFC3162]";
+		/* 101       Error-Cause Attribute                    [RFC3576] */
+	if ( c == 101) return "Error-Cause Attribute[RFC3576]";
+		/* 102       EAP-Key-Name                             [RFC4072] */
+	if ( c == 102) return "EAP-Key-Name [RFC4072]";
+		/* 103       Digest-Response                          [RFC5090] */
+	if ( c == 103) return "Digest-Response [RFC5090]";
+		/* 104       Digest-Realm                             [RFC5090] */
+	if ( c == 104) return "Digest-Realm [RFC5090]";
+		/* 105       Digest-Nonce                             [RFC5090]   */
+	if ( c == 105) return "Digest-Nonce [RFC5090]";
+		/* 106       Digest-Response-Auth                     [RFC5090] */
+	if ( c == 106) return "Digest-Response-Auth [RFC5090]";
+		/* 107       Digest-Nextnonce                         [RFC5090] */
+	if ( c == 107) return "Digest-Nextnonce [RFC5090]";
+		/* 108       Digest-Method                            [RFC5090] */
+	if ( c == 108) return "Digest-Method [RFC5090]";
+		/* 109       Digest-URI                               [RFC5090]  */
+	if ( c == 109) return "Digest-URI [RFC5090]";
+		/* 110       Digest-Qop                               [RFC5090]  */
+	if ( c == 110) return "Digest-Qop [RFC5090]";
+		/* 111       Digest-Algorithm                         [RFC5090]  */
+	if ( c == 111) return "Digest-Algorithm [RFC5090]";
+		/* 112       Digest-Entity-Body-Hash                  [RFC5090]  */
+	if ( c == 112) return "Digest-Entity-Body-Hash [RFC5090]";
+		/* 113       Digest-CNonce                            [RFC5090]  */
+	if ( c == 113) return "Digest-CNonce [RFC5090]";
+		/* 114       Digest-Nonce-Count                       [RFC5090]  */
+	if ( c == 114) return "Digest-Nonce-Count [RFC5090]";
+		/* 115       Digest-Username                          [RFC5090]  */
+	if ( c == 115) return "Digest-Username [RFC5090]";
+		/* 116       Digest-Opaque                            [RFC5090]  */
+	if ( c == 116) return "Digest-Opaque [RFC5090]";
+		/* 117       Digest-Auth-Param                        [RFC5090]  */
+	if ( c == 117) return "Digest-Auth-Param [RFC5090]";
+		/* 118       Digest-AKA-Auts                          [RFC5090]  */
+	if ( c == 118) return "Digest-AKA-Auts [RFC5090]";
+		/* 119       Digest-Domain                            [RFC5090]  */
+	if ( c == 119) return "Digest-Domain [RFC5090]";
+		/* 120       Digest-Stale                             [RFC5090]  */
+	if ( c == 120) return "Digest-Stale [RFC5090]";
+		/* 121       Digest-HA1                               [RFC5090]  */
+	if ( c == 121) return "Digest-HA1 [RFC5090]";
+		/* 122       SIP-AOR                                  [RFC5090]  */
+	if ( c == 122) return "SIP-AOR [RFC5090]";
+		/* 123       Delegated-IPv6-Prefix                    [RFC4818] */
+	if ( c == 123) return "Delegated-IPv6-Prefix [RFC4818]";
+		/* 124       MIP6-Feature-Vector                      [RFC5447] */
+	if ( c == 124) return "MIP6-Feature-Vector [RFC5447]";
+		/* 125       MIP6-Home-Link-Prefix                    [RFC5447] */
+	if ( c == 125) return "MIP6-Home-Link-Prefix [RFC5447]";
+		/* 126-191   Unassigned */
+	if ((c >= 126) && (c <= 191)) return "Unassigned ";
+		/* 192-223   Experimental Use                         [RFC3575] */
+	if ((c >= 192) && (c <= 223)) return "Experimental Use[RFC3575]";
+		/* 224-240   Implementation Specific                  [RFC3575] */
+	if ((c >= 224) && (c <= 240)) return "Implementation Specific[RFC3575]";
+		/* 241-255   Reserved                                 [RFC3575]    */
+	if ((c >= 241) && (c <= 255)) return "Reserved [RFC3575]";
+	/* fallback */ return "[Unknown]";
+}
Index: extensions/app_radgw/rgw_msg_codes.c
===================================================================
--- extensions/app_radgw/rgw_msg_codes.c	(revision 254)
+++ extensions/app_radgw/rgw_msg_codes.c	(revision 254)
@@ -0,0 +1,82 @@
+/*  Name of RADIUS command from its command code */
+const char * rgw_msg_code_str(unsigned char c) {
+		/* 1        Access-Request                           [RFC2865] */
+	if ( c == 1) return "Access-Request [RFC2865]";
+		/* 2        Access-Accept                            [RFC2865] */
+	if ( c == 2) return "Access-Accept [RFC2865]";
+		/* 3        Access-Reject                            [RFC2865] */
+	if ( c == 3) return "Access-Reject [RFC2865]";
+		/* 4        Accounting-Request                       [RFC2865] */
+	if ( c == 4) return "Accounting-Request [RFC2865]";
+		/* 5        Accounting-Response                      [RFC2865] */
+	if ( c == 5) return "Accounting-Response [RFC2865]";
+		/* 6        Accounting-Status                        [RFC3575] */
+	if ( c == 6) return "Accounting-Status [RFC3575]";
+		/* 7        Password-Request                         [RFC3575] */
+	if ( c == 7) return "Password-Request [RFC3575]";
+		/* 8        Password-Ack                             [RFC3575] */
+	if ( c == 8) return "Password-Ack [RFC3575]";
+		/* 9        Password-Reject                          [RFC3575] */
+	if ( c == 9) return "Password-Reject [RFC3575]";
+		/* 10       Accounting-Message                       [RFC3575] */
+	if ( c == 10) return "Accounting-Message [RFC3575]";
+		/* 11       Access-Challenge                         [RFC2865] */
+	if ( c == 11) return "Access-Challenge [RFC2865]";
+		/* 12       Status-Server (experimental)             [RFC2865] */
+	if ( c == 12) return "Status-Server (experimental)[RFC2865]";
+		/* 13       Status-Client (experimental)             [RFC2865] */
+	if ( c == 13) return "Status-Client (experimental)[RFC2865]";
+		/* 21       Resource-Free-Request                    [RFC3575] */
+	if ( c == 21) return "Resource-Free-Request [RFC3575]";
+		/* 22       Resource-Free-Response                   [RFC3575] */
+	if ( c == 22) return "Resource-Free-Response [RFC3575]";
+		/* 23       Resource-Query-Request                   [RFC3575] */
+	if ( c == 23) return "Resource-Query-Request [RFC3575]";
+		/* 24       Resource-Query-Response                  [RFC3575] */
+	if ( c == 24) return "Resource-Query-Response [RFC3575]";
+		/* 25       Alternate-Resource-Reclaim-Request       [RFC3575] */
+	if ( c == 25) return "Alternate-Resource-Reclaim-Request [RFC3575]";
+		/* 26       NAS-Reboot-Request                       [RFC3575] */
+	if ( c == 26) return "NAS-Reboot-Request [RFC3575]";
+		/* 27       NAS-Reboot-Response                      [RFC3575] */
+	if ( c == 27) return "NAS-Reboot-Response [RFC3575]";
+		/* 28       Reserved */
+	if ( c == 28) return "Reserved ";
+		/* 29       Next-Passcode                            [RFC3575] */
+	if ( c == 29) return "Next-Passcode [RFC3575]";
+		/* 30       New-Pin                                  [RFC3575] */
+	if ( c == 30) return "New-Pin [RFC3575]";
+		/* 31       Terminate-Session                        [RFC3575] */
+	if ( c == 31) return "Terminate-Session [RFC3575]";
+		/* 32       Password-Expired                         [RFC3575] */
+	if ( c == 32) return "Password-Expired [RFC3575]";
+		/* 33       Event-Request                            [RFC3575] */
+	if ( c == 33) return "Event-Request [RFC3575]";
+		/* 34       Event-Response                           [RFC3575] */
+	if ( c == 34) return "Event-Response [RFC3575]";
+		/* 40       Disconnect-Request                       [RFC3575][RFC5176] */
+	if ( c == 40) return "Disconnect-Request [RFC3575][RFC5176]";
+		/* 41       Disconnect-ACK                           [RFC3575][RFC5176] */
+	if ( c == 41) return "Disconnect-ACK [RFC3575][RFC5176]";
+		/* 42       Disconnect-NAK                           [RFC3575][RFC5176] */
+	if ( c == 42) return "Disconnect-NAK [RFC3575][RFC5176]";
+		/* 43       CoA-Request                              [RFC3575][RFC5176] */
+	if ( c == 43) return "CoA-Request [RFC3575][RFC5176]";
+		/* 44       CoA-ACK                                  [RFC3575][RFC5176] */
+	if ( c == 44) return "CoA-ACK [RFC3575][RFC5176]";
+		/* 45       CoA-NAK                                  [RFC3575][RFC5176] */
+	if ( c == 45) return "CoA-NAK [RFC3575][RFC5176]";
+		/* 50       IP-Address-Allocate                      [RFC3575] */
+	if ( c == 50) return "IP-Address-Allocate [RFC3575]";
+		/* 51       IP-Address-Release                       [RFC3575] */
+	if ( c == 51) return "IP-Address-Release [RFC3575]";
+		/* 52-249   Unassigned */
+	if ((c >= 52) && (c <= 249)) return "Unassigned ";
+		/* 250-253  Experimental Use                         [RFC3575] */
+	if ((c >= 250) && (c <= 253)) return "Experimental Use[RFC3575]";
+		/* 254      Reserved                                 [RFC3575] */
+	if ( c == 254) return "Reserved [RFC3575]";
+		/* 255      Reserved                                 [RFC3575] */
+	if ( c == 255) return "Reserved [RFC3575]";
+	/* fallback */ return "[Unknown]";
+}
Index: extensions/app_radgw/rgw_plugins.c
===================================================================
--- extensions/app_radgw/rgw_plugins.c	(revision 254)
+++ extensions/app_radgw/rgw_plugins.c	(revision 254)
@@ -0,0 +1,504 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Manage the list of plugins that provide handlers for RADIUS messages and attributes */
+
+#include "rgw.h"
+#include <dlfcn.h>
+#include <libgen.h>
+
+/* List of plugins, in the order they appear in the configuration file. */
+static struct fd_list plg_list = FD_LIST_INITIALIZER(plg_list);
+
+/* A plugin entry */
+struct plg_descr {
+	struct fd_list		 chain; 	/* chaining in plg_list */
+	
+	void 			*dlo;		/* pointer returned by dlopen for the extension, to use with dlclose later */
+	struct rgw_api		*descriptor;	/* Points to the resolved plugin's rgwp_descriptor */
+	struct rgwp_config 	*cs;		/* the (private) state returned by rgwp_conf_parse */
+	
+	int			 type;		/* this extension is called for messages received on this(these) server port(s) only */
+	unsigned char 		*cc;		/* array of command codes, or NULL for all cc */
+	size_t			 cc_len; 	/* size of the previous array */
+	
+	char 			*plgname; 	/* basename of the plugin, for debug messages. To be freed when object is detroyed */
+	char  			*conffile; 	/* configuration file passed to the extension, or "(null)". To be freed when object is destroyed */
+};
+
+/* Accelerators for each command code (one for each port). These accelerators are built on-demand, as a cache, after start_cache function has been called.  */
+static struct fd_list plg_accel_auth = FD_LIST_INITIALIZER(plg_accel_auth);
+static struct fd_list plg_accel_acct = FD_LIST_INITIALIZER(plg_accel_acct);
+
+/* Accelerator list item, one per command code value (only the ones actually used) */
+struct plg_accel {
+	struct fd_list		chain;	/* link in the plg_accel_* list. List ordered by ccode. */
+	unsigned char		ccode;	/* the command code for this accelerator. We don't handle extended CC yet */
+	struct fd_list		plugins;/* head for the list of plg_accel_item, corresponding to the extensions to be called for this command code. */
+};
+
+/* Accelerator item */
+struct plg_accel_item {
+	struct fd_list		chain; 	/* link in the plg_accel "plugins" list */
+	struct plg_descr *	plg;	/* pointer to the plugin */
+	/* Note: we can further optimize by caching the location of plg->descriptor->rgwp_rad_req etc... at this level. */
+};
+
+/* RWlock to protect all the previous lists */
+static pthread_rwlock_t plg_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+/* Has start_cache been called? */
+static int cache_started = 0;
+
+
+/* The read lock must be held before calling this function */
+static int get_accelerator(struct fd_list ** list, unsigned char ccode, int type)
+{
+	struct fd_list *refer, *search;
+	struct plg_accel * accel = NULL;
+	struct plg_accel_item * item = NULL;
+	int upgraded = 0;
+	
+	TRACE_ENTRY("%p %hhu %i", list, ccode, type);
+	
+	CHECK_PARAMS( cache_started && list && ((type == RGW_PLG_TYPE_AUTH) || (type == RGW_PLG_TYPE_ACCT)) );
+	
+	if (type == RGW_PLG_TYPE_AUTH)
+		refer = &plg_accel_auth;
+	else
+		refer = &plg_accel_acct;
+restart:	
+	/* Check if we have already an accelerator for this ccode */
+	for (search = refer->next; search != refer; search = search->next) {
+		struct plg_accel * loc = (struct plg_accel *)search;
+		
+		if (loc->ccode < ccode)
+			continue;
+		
+		if (loc->ccode > ccode)
+			break; /* we don't have an accelerator for this value yet */
+		
+		/* We found the matching accelerator, just return this list */
+		*list = &loc->plugins;
+		return 0;
+	}
+	
+	/* We must create the accelerator list, then save it just before "search" */
+	
+	/* First , upgrade the lock to write lock, and restart the search. This is the only robust solution to avoid deadlocks */
+	if (! upgraded) {
+		CHECK_POSIX( pthread_rwlock_unlock(&plg_lock) );
+		CHECK_POSIX( pthread_rwlock_wrlock(&plg_lock) );
+		upgraded = 1;
+		goto restart;
+	}
+	
+	/* Now create the new element */
+	CHECK_MALLOC( accel = malloc(sizeof(struct plg_accel)) );
+	memset(accel, 0, sizeof(struct plg_accel) );
+	fd_list_init(&accel->chain, NULL);
+	fd_list_init(&accel->plugins, accel);
+	accel->ccode = ccode;
+	
+	/* Check each extension from the global list for this port and ccode */
+	for (refer = plg_list.next; refer != &plg_list; refer = refer->next) {
+		struct plg_descr * loc = (struct plg_descr *)refer;
+		
+		/* Skip if this extension is not registered for this port */
+		if (! (loc->type & type) )
+			continue;
+		
+		/* Check if the ccode is there */
+		if (loc->cc) {
+			int i;
+			int match = 0;
+			for (i=0; i< loc->cc_len; i++) {
+				if (loc->cc[i] < ccode)
+					continue;
+				if (loc->cc[i] == ccode)
+					match = 1;
+				break;
+			}
+			if (!match)
+				continue;
+		}
+		
+		/* Ok, this extension must be called for this port / ccode, add to the accelerator */
+		CHECK_MALLOC( item = malloc(sizeof(struct plg_accel_item)) );
+		memset(item, 0, sizeof(struct plg_accel_item));
+		fd_list_init(&item->chain, NULL);
+		item->plg = loc;
+		/* Add as last element of the accelerator */
+		fd_list_insert_before(&accel->plugins, &item->chain);
+	}
+	
+	/* Now, save this accelerator entry in the global list */
+	fd_list_insert_before(search, &accel->chain);
+	*list = &accel->plugins;
+	
+	return 0;
+}
+
+
+int rgw_plg_add( char * plgfile, char * conffile, int type, unsigned char ** codes_array, size_t codes_sz )
+{
+	struct plg_descr * new;
+	int ret = 0;
+	char * tmp;
+	
+	TRACE_ENTRY("%p %p %d %p %zi", plgfile, conffile, type, codes_array, codes_sz);
+	
+	CHECK_PARAMS( plgfile && type && codes_array && (cache_started == 0) );
+	
+	CHECK_MALLOC( tmp = strdup(plgfile) );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct plg_descr)) );
+	memset(new, 0, sizeof(struct plg_descr));
+	
+	fd_list_init(&new->chain, new);
+	
+	/* Copy names, for debug */
+	CHECK_MALLOC( new->plgname = strdup(basename(tmp)) ); /* basename is a stupid function :( */
+	free(tmp);
+	CHECK_MALLOC( new->conffile = conffile ?: strdup("(null)") );
+	
+	/* Try and load the plugin */
+	TRACE_DEBUG(FULL, "Loading plugin: %s", plgfile);
+	new->dlo = dlopen(plgfile, RTLD_NOW | RTLD_GLOBAL);
+	if (new->dlo == NULL) {
+		/* An error occured */
+		fd_log_debug("Loading of plugin '%s' failed:\n %s\n", plgfile, dlerror());
+		goto error;
+	}
+	
+	/* Resolve the descriptor */
+	new->descriptor = dlsym( new->dlo, "rgwp_descriptor" );
+	if (new->descriptor == NULL) {
+		/* An error occured */
+		fd_log_debug("Unable to resolve 'rgwp_descriptor' in plugin '%s':\n %s\n", plgfile, dlerror());
+		goto error;
+	}
+	
+	/* Now parse the configuration file, this will initialize all extension states and store it in the returned pointer (the subextensions must be re-entrant) */
+	if (new->descriptor->rgwp_conf_parse) {
+		TRACE_DEBUG(FULL, "Parsing plugin conf file: %s", new->conffile );
+		new->cs = (*(new->descriptor->rgwp_conf_parse))(conffile);
+		if (new->cs == NULL) {
+			fd_log_debug("An error occurred while parsing configuration file '%s' in plugin '%s', aborting...\n", new->conffile, new->plgname);
+			goto error;
+		}
+	}
+	
+	/* Now sort the array (very simple algorithm, but this list is usually small) of command codes and save */
+	if (*codes_array && codes_sz) {
+		int i;
+		
+		new->cc = *codes_array;
+		*codes_array = NULL;
+		
+		for (i = 0; i < codes_sz - 1; i++) {
+			int j, idx = i, min = new->cc[i];
+			
+			/* find the smallest remaining element */
+			for (j = i + 1; j < codes_sz; j++) {
+				if (min > new->cc[j]) {
+					min = new->cc[j];
+					idx = j;
+				}
+			}
+			
+			/* swap if needed */
+			if (idx != i) {
+				int tmp = new->cc[i];
+				new->cc[i] = new->cc[idx];
+				new->cc[idx] = tmp;
+			}
+		}
+		new->cc_len = codes_sz;
+	}
+		
+	new->type = type;
+	
+	/* And save this new extension in the list. We don't need to lock at this point because we are single threaded. */
+	fd_list_insert_before(&plg_list, &new->chain);
+	
+	return 0;
+	
+	
+error:
+	if (new && new->dlo)
+		dlclose(new->dlo);
+	if (new)
+		free(new);	
+	return EINVAL;
+}
+
+void rgw_plg_dump(void)
+{
+	struct plg_descr * plg;
+	struct fd_list * ptr, *ptraccel;
+	
+	if ( ! TRACE_BOOL(FULL) )
+		return;
+	
+	CHECK_POSIX_DO( pthread_rwlock_rdlock(&plg_lock), );
+	
+	if ( ! FD_IS_LIST_EMPTY( &plg_list ) )
+		fd_log_debug("[app_radgw]  --- List of registered plugins:\n");
+	for (ptr = plg_list.next; ptr != &plg_list; ptr = ptr->next) {
+		
+		plg = (struct plg_descr *)ptr;
+		
+		fd_log_debug("  %-25s ( %-25s ) - types: %s%s, codes: ", 
+				plg->plgname, 
+				basename(plg->conffile),
+				plg->type & RGW_PLG_TYPE_AUTH ? "Au" : "  ",
+				plg->type & RGW_PLG_TYPE_ACCT ? "Ac" : "  ");
+		
+		if (plg->cc) {
+			int i;
+			
+			for (i = 0; i < plg->cc_len; i++) {
+				fd_log_debug("%02hhx ", plg->cc[i]);
+			}
+			fd_log_debug("\n");
+		} else {
+			fd_log_debug("*\n");
+		}
+	}
+	
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&plg_lock), );
+	
+	/* Dump the list of accelerators */
+	if ( ! TRACE_BOOL(FULL + 1) )
+		return;
+	
+	CHECK_POSIX_DO( pthread_rwlock_rdlock(&plg_lock), );
+	if ( !FD_IS_LIST_EMPTY( &plg_accel_auth ) || !FD_IS_LIST_EMPTY( &plg_accel_acct ))
+		fd_log_debug("  --- Accelerators:\n");
+	
+	for (ptraccel = plg_accel_auth.next; ptraccel != &plg_accel_auth; ptraccel = ptraccel->next) {
+		struct plg_accel * accel = (struct plg_accel *)ptraccel;
+		fd_log_debug("  auth, code %02hhu:\n", accel->ccode);
+
+		for (ptr = accel->plugins.next; ptr != &accel->plugins; ptr = ptr->next) {
+			struct plg_accel_item * item = (struct plg_accel_item *)ptr;
+			fd_log_debug("     %-15s (%s)\n", item->plg->plgname, basename(item->plg->conffile));
+		}
+	}
+	for (ptraccel = plg_accel_acct.next; ptraccel != &plg_accel_acct; ptraccel = ptraccel->next) {
+		struct plg_accel * accel = (struct plg_accel *)ptraccel;
+		fd_log_debug("  acct, code %02hhu:\n", accel->ccode);
+
+		for (ptr = accel->plugins.next; ptr != &accel->plugins; ptr = ptr->next) {
+			struct plg_accel_item * item = (struct plg_accel_item *)ptr;
+			fd_log_debug("     %-15s (%s)\n", item->plg->plgname, basename(item->plg->conffile));
+		}
+	}
+	
+	
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&plg_lock), );
+	
+}
+
+void rgw_plg_start_cache(void)
+{
+	cache_started++;
+}
+
+int rgw_plg_loop_req(struct rgw_radius_msg_meta **rad, struct session **session, struct msg **diam_msg, struct rgw_client * cli)
+{
+	int ret = 0;
+	struct fd_list * head = NULL, *li;
+	struct radius_msg * rad_ans = NULL;
+	
+	TRACE_ENTRY("%p %p %p %p", rad, session, diam_msg, cli);
+	CHECK_PARAMS( rad && *rad && session && diam_msg && *diam_msg && cli);
+	
+	/* First, get the list of extensions for this message */
+	CHECK_POSIX( pthread_rwlock_rdlock( &plg_lock) );
+	CHECK_FCT_DO( ret = get_accelerator(&head, (*rad)->radius.hdr->code, (*rad)->serv_type), 
+		{ CHECK_POSIX( pthread_rwlock_unlock( &plg_lock) ); return ret; } );
+	
+	/* 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;
+		
+		if (plg->descriptor->rgwp_rad_req) {
+			TRACE_DEBUG(ANNOYING, "Calling next plugin: %s", plg->plgname);
+			ret = (*plg->descriptor->rgwp_rad_req)(plg->cs, *session, &(*rad)->radius, &rad_ans, diam_msg, cli);
+			if (ret)
+				break;
+		} else {
+			TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", plg->plgname);
+		}					
+	}
+	
+	CHECK_POSIX( pthread_rwlock_unlock( &plg_lock) );
+	
+	/* If no error encountered, we're done here */
+	if (ret == 0)
+		return 0;
+	
+	/* Destroy the Diameter temp message, if any */
+	if (*diam_msg) {
+		CHECK_FCT_DO( fd_msg_free(*diam_msg), );
+		*diam_msg = NULL;
+	}
+	
+	/* Destroy the session, there won't be a reply message to retrieve the data */
+	if (*session) {
+		CHECK_FCT_DO( fd_sess_destroy(session), );
+	}
+	
+	/* Send the radius message back if required */
+	if ((ret == -2) && rad_ans && rad) {
+		CHECK_FCT_DO( rgw_client_finish_send(&rad_ans, *rad, cli), /* It failed, it can't be helped... */);
+	}
+	
+	if (ret > 0) {
+		/* Critical error, log and exit */
+		fd_log_debug("An error occurred while handling a RADIUS message, turn on DEBUG for details: %s\n", strerror(ret));
+		return ret;
+	}
+	
+	/* Now, discard the message and return */
+	rgw_msg_free(rad);
+	return 0;
+}
+
+/* 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 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);
+	
+	/* Get the list of extensions of the RADIUS request */
+	CHECK_POSIX( pthread_rwlock_rdlock( &plg_lock) );
+	CHECK_FCT_DO( ret = get_accelerator(&head, req->radius.hdr->code, req->serv_type), 
+		{ CHECK_POSIX( pthread_rwlock_unlock( &plg_lock) ); return ret; } );
+	
+	/* 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;
+		
+		if (plg->descriptor->rgwp_diam_ans) {
+			TRACE_DEBUG(ANNOYING, "Calling next plugin: %s", plg->plgname);
+			ret = (*plg->descriptor->rgwp_diam_ans)(plg->cs, session, diam_ans, rad_ans, (void *)cli);
+			if (ret)
+				break;
+		} else {
+			TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", plg->plgname);
+		}					
+	}
+	
+	CHECK_POSIX( pthread_rwlock_unlock( &plg_lock) );
+	
+	/* If no error encountered, we're done here */
+	if (ret == 0)
+		return 0;
+	
+	/* Destroy the temporary RADIUS answer */
+	if (*rad_ans) {
+		radius_msg_free(*rad_ans);
+		free(*rad_ans);
+		*rad_ans = NULL;
+	}
+	
+	if (ret > 0) {
+		/* Critical error, log and exit */
+		fd_log_debug("[app_radgw] An error occurred while handling a DIAMETER answer to a converted RADIUS request, turn on DEBUG for details: %s\n", strerror(ret));
+		return ret;
+	}
+	
+	/* We might define other return values with special meaning here (ret == -1, ...) for example create a new Diameter request */
+	
+	return 0;
+}
+
+void rgw_plg_fini(void)
+{
+	struct fd_list * item, *subitem;
+	
+	TRACE_ENTRY();
+	
+	CHECK_POSIX_DO( pthread_rwlock_rdlock( &plg_lock), /* continue anyway */ );
+	
+	/* Remove all elements from all accelerators */
+	while ( ! FD_IS_LIST_EMPTY(&plg_accel_auth) ) {
+		item = plg_accel_auth.next;
+		fd_list_unlink(item);
+		{
+			struct plg_accel * accel = (struct plg_accel *)item;
+			while ( ! FD_IS_LIST_EMPTY(&accel->plugins) ) {
+				subitem = accel->plugins.next;
+				fd_list_unlink(subitem);
+				free(subitem);
+			}
+		}
+		free(item);
+	}
+	while ( ! FD_IS_LIST_EMPTY(&plg_accel_acct) ) {
+		item = plg_accel_acct.next;
+		fd_list_unlink(item);
+		{
+			struct plg_accel * accel = (struct plg_accel *)item;
+			while ( ! FD_IS_LIST_EMPTY(&accel->plugins) ) {
+				subitem = accel->plugins.next;
+				fd_list_unlink(subitem);
+				free(subitem);
+			}
+		}
+		free(item);
+	}
+	
+	/* Now destroy all plugins information */
+	while ( ! FD_IS_LIST_EMPTY(&plg_list) ) {
+		struct plg_descr * plg = (struct plg_descr *) plg_list.next;
+		fd_list_unlink(&plg->chain);
+		free(plg->conffile);
+		free(plg->plgname);
+		free(plg->cc);
+		if (plg->cs && plg->descriptor && plg->descriptor->rgwp_conf_free )
+			(*plg->descriptor->rgwp_conf_free)(plg->cs);
+		dlclose(plg->dlo);
+		free(plg);
+	}
+	
+	CHECK_POSIX_DO( pthread_rwlock_unlock( &plg_lock), );
+}
Index: extensions/app_radgw/rgw_servers.c
===================================================================
--- extensions/app_radgw/rgw_servers.c	(revision 254)
+++ extensions/app_radgw/rgw_servers.c	(revision 254)
@@ -0,0 +1,308 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Manage the RADIUS server(s): opening sockets, receiving messages, ... */
+
+#include "rgw.h"
+
+#define RADIUS_MAX_MSG_LEN 	3000
+#define RADIUS_AUTH_PORT	1812
+#define RADIUS_ACCT_PORT	1813
+
+/* Declare the rgw_servers */
+struct rgw_servs rgw_servers;
+
+void rgw_servers_dump(void)
+{
+	char ipstr[INET6_ADDRSTRLEN];
+	
+	if ( ! TRACE_BOOL(FULL) )
+		return;
+	
+	fd_log_debug(" auth server:\n");
+	fd_log_debug("    disabled..... : %s\n", rgw_servers.auth_serv.disabled ? "TRUE":"false");
+	fd_log_debug("    IP disabled.. : %s\n", rgw_servers.auth_serv.ip_disabled ? "TRUE":"false");
+	fd_log_debug("    IPv6 disabled : %s\n", rgw_servers.auth_serv.ip6_disabled ? "TRUE":"false");
+	fd_log_debug("    port......... : %hu\n", ntohs(rgw_servers.auth_serv.port));
+	inet_ntop(AF_INET, &rgw_servers.auth_serv.ip_endpoint,ipstr,sizeof(ipstr));
+	fd_log_debug("    IP bind...... : %s\n", ipstr);
+	inet_ntop(AF_INET6, &rgw_servers.auth_serv.ip6_endpoint,ipstr,sizeof(ipstr));
+	fd_log_debug("    IPv6 bind.... : %s\n", ipstr);
+
+	fd_log_debug(" acct server:\n");
+	fd_log_debug("    disabled..... : %s\n", rgw_servers.acct_serv.disabled ? "TRUE":"false");
+	fd_log_debug("    IP disabled.. : %s\n", rgw_servers.acct_serv.ip_disabled ? "TRUE":"false");
+	fd_log_debug("    IPv6 disabled : %s\n", rgw_servers.acct_serv.ip6_disabled ? "TRUE":"false");
+	fd_log_debug("    port......... : %hu\n", ntohs(rgw_servers.acct_serv.port));
+	inet_ntop(AF_INET, &rgw_servers.acct_serv.ip_endpoint,ipstr,sizeof(ipstr));
+	fd_log_debug("    IP bind...... : %s\n", ipstr);
+	inet_ntop(AF_INET6, &rgw_servers.acct_serv.ip6_endpoint,ipstr,sizeof(ipstr));
+	fd_log_debug("    IPv6 bind.... : %s\n", ipstr);
+
+}
+
+static struct servers_data {
+	int	type; /* auth or acct */
+	int	family; /* AF_INET or AF_INET6 */
+	int	sock; /* the socket number */
+	pthread_t th; /* the running server thread, or NULL */
+	char    name[10];
+} SERVERS[4];
+
+int rgw_servers_init(void)
+{
+	memset(&rgw_servers, 0, sizeof(rgw_servers));
+	memset(&SERVERS[0], 0, sizeof(SERVERS));
+
+	rgw_servers.auth_serv.port = htons(RADIUS_AUTH_PORT);
+	rgw_servers.acct_serv.port = htons(RADIUS_ACCT_PORT);
+	
+	return 0;
+}
+
+static void * server_thread(void * param)
+{
+	struct servers_data * me = (struct servers_data *)param;
+	
+	TRACE_ENTRY("%p", param);
+	
+	CHECK_PARAMS_DO(param, return NULL);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "radgw/%s serv", me->name);
+		fd_log_threadname ( buf );
+	}
+	
+	/* Now loop on this socket, parse and queue each message received, until thread is cancelled. */
+	while (1) {
+		struct sockaddr_storage from;
+		socklen_t fromlen = sizeof(from);
+		int len;
+		struct rgw_client * nas_info = NULL;
+		uint16_t port = 0;
+		unsigned char buf[RADIUS_MAX_MSG_LEN];
+		struct rgw_radius_msg_meta *msg = NULL;
+		
+		pthread_testcancel();
+		
+		/* receive the next message */
+		CHECK_SYS_DO( len = recvfrom( me->sock, &buf[0], sizeof(buf), 0, (struct sockaddr *) &from, &fromlen),  break );
+		
+		/* Get the port */
+		if (from.ss_family == AF_INET)
+			port = ((struct sockaddr_in *)&from)->sin_port;
+		if (from.ss_family == AF_INET6)
+			port = ((struct sockaddr_in6 *)&from)->sin6_port;
+		if (!port) {
+			TRACE_DEBUG(INFO, "Invalid port (family: %d), discarding received %d bytes...", from.ss_family, len);
+			continue;
+		}
+		
+		TRACE_DEBUG(FULL, "Received %d bytes", len);
+		TRACE_DEBUG_sSA(FULL, " from ", &from, NI_NUMERICHOST | NI_NUMERICSERV, "\n" );
+		
+		/* Search the associated client definition, if any */
+		CHECK_FCT_DO( rgw_clients_search((struct sockaddr *) &from, &nas_info),
+			{
+				TRACE_DEBUG(INFO, "Discarding %d bytes received from unknown IP:", len);
+				TRACE_DEBUG_sSA(INFO, " ", &from, NI_NUMERICHOST | NI_NUMERICSERV, "\n" );
+				continue;
+			} );
+				
+		
+		/* parse the message, loop if message is invalid */
+		CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg), 
+			{
+				char * cliname = NULL;
+				CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, NULL), );
+				TRACE_DEBUG(INFO, "Discarding invalid RADIUS message from '%s'", cliname);
+				rgw_clients_dispose(&nas_info);
+				continue; 
+			} );
+		
+		msg->serv_type = me->type;
+		msg->port = port;
+		
+		rgw_msg_dump(msg);
+		
+		/* queue the message for a worker thread */
+		CHECK_FCT_DO( rgw_work_add(msg, nas_info), break );
+		
+		/* Then wait for next incoming message */
+	}
+	
+	TRACE_DEBUG(INFO, "Server thread terminated.");
+	return NULL;
+}
+
+/* Set the socket options for UDP sockets, before bind is called */
+static int _udp_setsockopt(int family, int sk)
+{
+	int ret = 0;
+	int opt;
+	
+	/* In case of v6 address, force the v6only option, we use a different socket for v4 */
+	#ifdef IPV6_V6ONLY
+	if (family == AF_INET6) {
+		opt = 1;
+		ret = setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket IPV6_V6ONLY option: %s", strerror(ret));
+			return ret;
+		}
+	}
+	#endif /* IPV6_V6ONLY */
+	
+	return 0;
+}
+
+/* We reuse the same logic for all 4 possible servers (IP / IPv6, Auth / Acct ports) */
+#define UDPSERV( _type_, _portval_, _family_ ) {								\
+	/* Check that this type / family are not disabled by configuration */					\
+	if ( (! rgw_servers. _type_ ## _serv.disabled) 								\
+		&& ( ! rgw_servers. _type_ ## _serv.ip ## _family_ ## _disabled ) ) {				\
+			struct sockaddr_in ## _family_	 sin ## _family_ ;					\
+			/* Create the socket */									\
+			CHECK_SYS( SERVERS[idx].sock = socket(AF_INET ## _family_, SOCK_DGRAM, 0) );		\
+			/* Set the parameters for bind into "sin" or "sin6" */					\
+			memset(& sin ## _family_, 0, sizeof(struct sockaddr_in ## _family_));			\
+			sin ## _family_ . sin ## _family_ ## _family = AF_INET ## _family_;			\
+			sin ## _family_ . sin ## _family_ ## _port = rgw_servers. _type_ ## _serv . port;	\
+			memcpy( &sin ## _family_ .sin ## _family_ ## _addr, 					\
+					&rgw_servers. _type_ ## _serv . ip ## _family_ ## _endpoint,		\
+					sizeof(struct in ## _family_ ## _addr) );				\
+			/* This sockopt must be set before binding */						\
+			TRACE_DEBUG(ANNOYING, "Setting socket options...");					\
+			CHECK_FCT( _udp_setsockopt(AF_INET ## _family_, SERVERS[idx].sock) );			\
+			/* OK, now, bind */									\
+			TRACE_DEBUG(ANNOYING, "Binding " #_type_ " ip" #_family_ " server...");			\
+			CHECK_SYS( bind( SERVERS[idx].sock,							\
+					(struct sockaddr *)&sin ## _family_,					\
+					sizeof(struct sockaddr_in ## _family_) ) );				\
+			/* Save the server information in SERVERS structure */					\
+			SERVERS[idx].type = _portval_;								\
+			SERVERS[idx].family = AF_INET ## _family_;						\
+			snprintf(&SERVERS[idx].name[0], sizeof(SERVERS[idx].name), # _type_ "/ip" #_family_);	\
+			/* Create the server thread */								\
+			CHECK_POSIX( pthread_create(&SERVERS[idx].th, NULL, server_thread, &SERVERS[idx]) );	\
+			idx++;											\
+	}													\
+}
+
+int rgw_servers_start(void)
+{
+	int idx = 0;
+	
+	TRACE_ENTRY();
+	
+	UDPSERV( auth, RGW_PLG_TYPE_AUTH,  );
+	UDPSERV( auth, RGW_PLG_TYPE_AUTH, 6 );
+	UDPSERV( acct, RGW_PLG_TYPE_ACCT,  );
+	UDPSERV( acct, RGW_PLG_TYPE_ACCT, 6 );
+	
+	TRACE_DEBUG(FULL, "%d UDP servers started succesfully.", idx);
+	return 0;
+}
+
+/* Send a RADIUS message */
+int rgw_servers_send(int type, unsigned char *buf, size_t buflen, struct sockaddr *to, uint16_t to_port)
+{
+	int idx = 0;
+	int ret = 0;
+	struct sockaddr_storage sto;
+	
+	/* Find the appropriate socket to use (not sure if it is important) */
+	for (idx = 0; idx < sizeof(SERVERS) / sizeof(SERVERS[0]); idx++) {
+		if ( SERVERS[idx].sock && (type == SERVERS[idx].type) && (to->sa_family == SERVERS[idx].family) ) {
+			ret = 1;
+			break;
+		}
+	}
+	
+	if (!ret) {
+		TRACE_DEBUG(INFO, "Trying to send a message from a disabled server: %s / %s", 
+				(type == RGW_PLG_TYPE_AUTH) ? "Auth" : "Acct",
+				(to->sa_family == AF_INET)  ? "IP (v4)" : "IPv6");
+		return EINVAL;
+	}
+	
+	/* Prepare the destination info */
+	memset(&sto, 0, sizeof(sto));
+	if (to->sa_family == AF_INET) {
+		memcpy(&sto, to, sizeof(struct sockaddr_in));
+		((struct sockaddr_in *)&sto)->sin_port = to_port;
+	} else {
+		memcpy(&sto, to, sizeof(struct sockaddr_in6));
+		((struct sockaddr_in6 *)&sto)->sin6_port = to_port;
+	}
+	
+	TRACE_DEBUG(FULL, "Sending %d bytes", buflen);
+	TRACE_DEBUG_sSA(FULL, " to ", &sto, NI_NUMERICHOST | NI_NUMERICSERV, "\n" );
+		
+	/* Send */
+	ret = sendto(SERVERS[idx].sock, buf, buflen, 0, (struct sockaddr *)&sto, sSAlen(&sto));
+	if (ret < 0) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "An error prevented sending of a RADIUS message: %s", strerror(ret));
+		return ret;
+	}
+	if (ret != buflen) {
+		TRACE_DEBUG(INFO, "Incomplete send: %d bytes / %zd", ret, buflen);
+		return EAGAIN;
+	}
+	
+	/* Done :) */
+	return 0;
+}
+
+void rgw_servers_fini(void)
+{
+	int idx = 0;
+	
+	for (idx = 0; idx < sizeof(SERVERS) / sizeof(SERVERS[0]); idx++) {
+		if (SERVERS[idx].sock == 0)
+			break;
+		
+		CHECK_FCT_DO( fd_thr_term(&SERVERS[idx].th), /* continue */ );
+		close(SERVERS[idx].sock);
+		SERVERS[idx].sock = 0;
+	}
+	
+}
+
+
Index: extensions/app_radgw/rgw_worker.c
===================================================================
--- extensions/app_radgw/rgw_worker.c	(revision 254)
+++ extensions/app_radgw/rgw_worker.c	(revision 254)
@@ -0,0 +1,351 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Manage incoming RADIUS messages. */
+
+#include "rgw.h"
+
+/* How many threads to handle messages in parallel ? */
+#define NB_WORKERS	2
+
+static pthread_t workers[NB_WORKERS];
+static struct fifo * work_stack = NULL;
+
+/* Data that is stacked */
+struct work_item {
+	struct rgw_radius_msg_meta * msg;
+	struct rgw_client 	   * cli;
+};
+
+/* Data stored in freeDiameter while pending Diameter answer */
+struct pending_answer {
+	struct rgw_radius_msg_meta * rad;  /* the RADIUS message that was received and translated */
+	struct rgw_client          * cli;  /* the client it was received from */
+	struct session 		   * sess; /* the Diameter session created for this message (useful?) */
+};
+
+/* Callback when a Diameter answer is received */
+static void receive_diam_answer(void * paback, struct msg **ans);
+
+/* Worker thread, processing incoming RADIUS messages (after parsing) */
+static void * work_th(void * arg)
+{
+	char thname[10];
+	
+	TRACE_ENTRY("%p", arg);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "radgw/worker #%d", (int)arg);
+		fd_log_threadname ( buf );
+	}
+	
+	while (1) { /* The thread will be cancelled */
+		
+		struct rgw_radius_msg_meta * msg;
+		struct rgw_client * cli;
+		struct session * session;
+		struct msg * diam_msg;
+		int pb, a;
+		struct pending_answer * pa;
+	
+		/* Get the next incoming RADIUS message */
+		{
+			struct work_item * wi = NULL;
+			
+			CHECK_FCT_DO( fd_fifo_get(work_stack, &wi), break );
+
+			msg = wi->msg;
+			cli = wi->cli;
+			free(wi);
+		}
+		
+		TRACE_DEBUG(ANNOYING, "Processing next RADIUS message: %p received on client: %p", msg, cli);
+	
+		/* process the data */
+		
+		/* Check authenticator, if any */
+		CHECK_FCT_DO( rgw_msg_auth_check(msg, cli, NULL),
+			{
+				/* An error occurred, discard message */
+				rgw_msg_free(&msg);
+				rgw_clients_dispose(&cli);
+				continue;
+			}  );
+		
+		/* Check duplicate */
+		CHECK_FCT_DO( rgw_clients_check_dup(&msg, cli),
+			{
+				/* An error occurred, discard message */
+				rgw_msg_free(&msg);
+				rgw_clients_dispose(&cli);
+				continue;
+			}  );
+		if (msg == NULL) {
+			rgw_clients_dispose(&cli);
+			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),
+			{
+				/* 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. */
+		
+		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),
+			{
+				/* An error occurred, discard message */
+				rgw_msg_free(&msg);
+				rgw_clients_dispose(&cli);
+				continue;
+			}  );
+		
+		/* Pass the message to the list of registered plugins */
+		CHECK_FCT_DO( rgw_plg_loop_req(&msg, &session, &diam_msg, cli), 
+			{
+				/* An error occurred, discard message */
+				rgw_msg_free(&msg);
+				rgw_clients_dispose(&cli);
+				continue;
+			}  );
+		if (msg == NULL) {
+			rgw_clients_dispose(&cli);
+			continue; /* the message was handled already */
+		}
+		
+		pb = 0;
+		
+		/* Check the created Diameter 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",
+					msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
+			/* We might also dump the conflicting rule here if useful */
+			pb++;
+		}
+		
+		/* Check if the full content of the RADIUS message was handled */
+		for (a = 0; a < msg->radius.attr_used; a++) {
+			struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[a]);
+			pb++;
+			fd_log_debug("[radgw] No plugin available to handle attribute %hhd (%s) in command %hhd (%s)! Translation aborted.\n",
+					attr->type, rgw_msg_attrtype_str(attr->type),
+					msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
+		}
+		
+		/* Check the session is correct */
+		ASSERT(session != NULL);
+		
+		if (pb) {
+			/* Something went wrong during the conversion */
+			if (session) {
+				CHECK_FCT_DO( fd_sess_destroy(&session), );
+			}
+			
+			if (diam_msg) {
+				CHECK_FCT_DO( fd_msg_free(diam_msg), );
+				diam_msg = NULL;
+			}
+			
+			rgw_msg_free(&msg);
+			rgw_clients_dispose(&cli);
+			
+			TRACE_DEBUG(INFO, "%d problem(s) occurred while translating a RADIUS message, data discarded.\n", pb);
+			continue;
+		}
+		
+		/* Send the Diameter message and register for receiving the answer */
+		CHECK_MALLOC_DO( pa = malloc(sizeof(struct pending_answer)), break );
+		memset(pa, 0, sizeof(*pa));
+		pa->rad = msg;
+		pa->cli = cli;
+		pa->sess= session;
+		
+		CHECK_FCT_DO( fd_msg_send( &diam_msg, receive_diam_answer, pa), 
+			{
+				/* If an error occurs, log and destroy the data */
+				fd_log_debug("An error occurred while sending Diameter message, please turn Debug on for detail.\n");
+				if (session) {
+					CHECK_FCT_DO( fd_sess_destroy(&session), );
+				}
+
+				if (diam_msg) {
+					CHECK_FCT_DO( fd_msg_free(diam_msg), );
+					diam_msg = NULL;
+				}
+
+				rgw_msg_free(&msg);
+				rgw_clients_dispose(&cli);
+				
+				free(pa);
+				
+				continue;
+			} );
+		
+		/* Done! */
+	}
+	
+	TRACE_DEBUG(INFO, "Thread terminated!");
+	return NULL;
+}
+
+static void receive_diam_answer(void * paback, struct msg **ans)
+{
+	struct pending_answer * pa = (struct pending_answer *)paback;
+	struct radius_msg * rad_ans;
+	struct avp *avp;
+	struct avp_hdr  *ahdr;
+	int pb = 0;
+	
+	TRACE_ENTRY("%p %p", pa, ans);
+	CHECK_PARAMS_DO( pa && ans, return );
+	
+	/* Create an empty RADIUS answer message */
+	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 );
+	
+	/* Now check what AVPs remain in the diameter answer. If AVPs with the 'M' flag are here, we have a problem... */
+	CHECK_FCT_DO( fd_msg_browse(*ans, MSG_BRW_FIRST_CHILD, &avp, NULL), { avp = NULL; pb++; } );
+	while (avp) {
+		CHECK_FCT_DO( fd_msg_avp_hdr ( avp, &ahdr ), { pb++; continue; } );
+		if (ahdr->avp_flags & AVP_FLAG_MANDATORY) {
+			if (ahdr->avp_flags & AVP_FLAG_VENDOR) {
+				TRACE_DEBUG(FULL, "Remaining Mandatory Vendor AVP, code %d", ahdr->avp_code);
+				pb++;
+			} else {
+				switch (ahdr->avp_code) {
+					/* A few AVPs can be safely ignored here: */
+					case DIAM_ATTR_ROUTE_RECORD:
+					case DIAM_ATTR_PROXY_INFO:
+						
+						
+						/* just ignore */
+						break;
+					
+					default:
+						TRACE_DEBUG(FULL, "Remaining Mandatory AVP, code %d", ahdr->avp_code);
+						pb++;
+				}
+			}
+		}
+		CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), { pb++; break; } );
+	}
+	
+	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);
+	}
+	
+	/* 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);	
+	}
+
+out:
+	/* Destroy remaining session data (stateless gateway) */
+	CHECK_FCT_DO( fd_sess_destroy(&pa->sess),  );
+	
+	/* Clear the Diameter message */
+	if (*ans) {
+		CHECK_FCT_DO( fd_msg_free(*ans),  );
+		*ans = NULL;
+	}
+	
+	/* Release reference on the client */
+	rgw_clients_dispose(&pa->cli);
+	
+	/* Clear the answer data */
+	free(pa);
+	
+	/* Finished */
+	return;
+}
+
+int rgw_work_start(void)
+{
+	int i;
+	TRACE_ENTRY();
+	
+	memset(workers, 0, sizeof(workers));
+	
+	CHECK_FCT( fd_fifo_new ( &work_stack ) );
+	
+	/* Create the worker thread(s) */
+	for (i = 0; i < NB_WORKERS; i++) {
+		CHECK_POSIX( pthread_create(&workers[i], NULL, work_th, (void *)i) );
+	}
+	
+	return 0;
+}
+
+int rgw_work_add(struct rgw_radius_msg_meta * msg, struct rgw_client * client)
+{
+	struct work_item * new;
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct work_item)) );
+	memset(new, 0, sizeof(struct work_item));
+	
+	new->msg = msg;
+	new->cli = client;
+	
+	CHECK_FCT( fd_fifo_post(work_stack, &new) );
+	
+	return 0;
+}
+
+void rgw_work_fini(void)
+{
+	int i;
+	TRACE_ENTRY();
+	
+	for (i = 0; i < NB_WORKERS; i++) {
+		fd_thr_term(&workers[i]);
+	}
+	
+	TODO("Empty the stack, what to do about the RADIUS messages?");
+	
+	return;
+}
