Initial commit

Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/extensions/app_radgw/CMakeLists.txt b/extensions/app_radgw/CMakeLists.txt
new file mode 100644
index 0000000..fb91c2b
--- /dev/null
+++ b/extensions/app_radgw/CMakeLists.txt
@@ -0,0 +1,118 @@
+# The app_radgw extension
+PROJECT("RADIUS/Diameter extensible gateway application for freeDiameter" C)
+
+
+########### 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
+	radius.c
+	md5.c
+	rgw_msg_codes.c
+	rgw_msg_attrtype.c
+	rgw_main.c
+	lex.rgw_conf.c
+	rgw_conf.tab.c
+	rgw_conf.tab.h
+	rgw_clients.c
+	rgw_plugins.c
+	rgw_servers.c
+	rgw_worker.c
+)
+
+SET( RG_COMMON_HEADER
+	rgw_common.h
+	radius.h
+	md5.h
+	hostap_compat.h
+)
+
+# Compile these files as a freeDiameter extension.
+FD_ADD_EXTENSION(app_radgw ${RGW_DEFAULT_SRC} ${RG_COMMON_HEADER})
+
+
+########### RADIUS/Diameter translation agent plugins (support for RADIUS protocol) ############
+# Use the macro RGWX_ADD_PLUGIN(name files...) to create a plugin.
+# It is equivalent to add_library with the appropriate parameters
+# and naming conventions (.rgwx : Radius GateWay eXtension)
+MACRO(RGWX_ADD_PLUGIN PLGNAME)
+  ADD_LIBRARY(${PLGNAME} MODULE ${ARGN})
+  SET_TARGET_PROPERTIES(${PLGNAME} PROPERTIES PREFIX "" )
+  SET_TARGET_PROPERTIES(${PLGNAME} PROPERTIES SUFFIX ".rgwx" )
+  INSTALL(TARGETS ${PLGNAME}
+	LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX}
+	COMPONENT freeDiameter-radius-gateway)
+ENDMACRO(RGWX_ADD_PLUGIN)
+
+# Ask unless ALL_EXTENSIONS is set:
+MACRO(FD_OPTION_PLUGIN PLGVAR DESCR DEFLT)
+   IF (NOT ALL_EXTENSIONS)
+     OPTION(BUILD_${PLGVAR} ${DESCR} ${DEFLT})
+   ENDIF (NOT ALL_EXTENSIONS)
+   IF (BUILD_${PLGVAR} OR ALL_EXTENSIONS)
+     SET(${PLGVAR} TRUE)
+   ELSE (BUILD_${PLGVAR} OR ALL_EXTENSIONS)
+     SET(${PLGVAR} FALSE)
+   ENDIF (BUILD_${PLGVAR} OR ALL_EXTENSIONS)
+ENDMACRO(FD_OPTION_PLUGIN PLGVAR DESCR DEFLT)
+
+
+### Debug 
+# Example of plugin:
+
+FD_OPTION_PLUGIN(RGWX_SAMPLE "Build sample plugin? (for developers only)" OFF)
+ 	IF (RGWX_SAMPLE)
+ 	   RGWX_ADD_PLUGIN(sample ${RG_COMMON_HEADER} rgwx_sample.c)
+ 	ENDIF (RGWX_SAMPLE)
+
+# A plugin for debug: dumps RADIUS and Diameter messages state at the time the plugin is called.
+FD_OPTION_PLUGIN(RGWX_DEBUG "Build debug plugin? (display status of RADIUS and Diameter messages)" ON)
+ 	IF (RGWX_DEBUG)
+ 	   RGWX_ADD_PLUGIN(debug ${RG_COMMON_HEADER} rgwx_debug.c)
+ 	ENDIF (RGWX_DEBUG)
+
+
+### Authentication, Authorization messages translation.
+FD_OPTION_PLUGIN(RGWX_AUTH "Build Authentication & Authorization RADIUS translation plugin? (RFC2865, RFC3579)" ON)
+	IF (RGWX_AUTH)
+ 	   RGWX_ADD_PLUGIN(auth ${RG_COMMON_HEADER} rgwx_auth.c)
+	ENDIF (RGWX_AUTH)
+
+### SIP Authentication, Authorization messages translation.
+FD_OPTION_PLUGIN(RGWX_SIP "Build SIP RADIUS translation plugin? (RFC4740 or RFC5090)" OFF)
+        IF (RGWX_SIP)
+           RGWX_ADD_PLUGIN(sip ${RG_COMMON_HEADER} rgwx_sip.c)
+        ENDIF (RGWX_SIP)
+
+
+
+### Accounting messages translation.
+FD_OPTION_PLUGIN(RGWX_ACCT "Build Accounting RADIUS translation plugin? (RFC2866)" ON)
+	IF (RGWX_ACCT)
+ 	   RGWX_ADD_PLUGIN(acct ${RG_COMMON_HEADER} rgwx_acct.c)
+	ENDIF (RGWX_ACCT)
+
+
+### Generic plugin to handle some attributes (either delete them or simply echo them in the answer) 
+FD_OPTION_PLUGIN(RGWX_ECHODROP "Build 'echo/drop' plugin? (drop specific RADIUS attributes or echo them in RADIUS answer)" ON)
+  	IF (RGWX_ECHODROP)
+  	   BISON_FILE(rgwx_echodrop.y)
+  	   FLEX_FILE(rgwx_echodrop.l)
+  	   SET_SOURCE_FILES_PROPERTIES(lex.rgwx_echodrop.c rgwx_echodrop.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+ 	   RGWX_ADD_PLUGIN(echodrop ${RG_COMMON_HEADER} rgwx_echodrop.h rgwx_echodrop.c lex.rgwx_echodrop.c rgwx_echodrop.tab.c rgwx_echodrop.tab.h )
+  	ENDIF (RGWX_ECHODROP)
+	
+	
+####
+## INSTALL section ##
+
+INSTALL(TARGETS app_radgw
+	LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX}
+	COMPONENT freeDiameter-radius-gateway)
+	
+# Note that all compiled plugins are added by the RGWX_ADD_PLUGIN macro in the component freeDiameter-radius-gateway as well.
diff --git a/extensions/app_radgw/hostap_compat.h b/extensions/app_radgw/hostap_compat.h
new file mode 100644
index 0000000..9bee2ac
--- /dev/null
+++ b/extensions/app_radgw/hostap_compat.h
@@ -0,0 +1,194 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, 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 */
diff --git a/extensions/app_radgw/md5.c b/extensions/app_radgw/md5.c
new file mode 100644
index 0000000..055fcf0
--- /dev/null
+++ b/extensions/app_radgw/md5.c
@@ -0,0 +1,399 @@
+/*********************************************************************************/
+/* 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 below.
+ *  In addition to this notice, only the #include directives have been modified.
+ */
+#include "rgw_common.h"
+/*********************************************************************************/
+
+ 
+/*
+ * 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 */
diff --git a/extensions/app_radgw/md5.h b/extensions/app_radgw/md5.h
new file mode 100644
index 0000000..bcbfdc3
--- /dev/null
+++ b/extensions/app_radgw/md5.h
@@ -0,0 +1,46 @@
+/*********************************************************************************/
+/* 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 below.
+ */
+
+/*********************************************************************************/
+
+/*
+ * 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 */
+
+ /* Forward declaration: */
+void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+#endif /* MD5_H */
diff --git a/extensions/app_radgw/radius.c b/extensions/app_radgw/radius.c
new file mode 100644
index 0000000..44755eb
--- /dev/null
+++ b/extensions/app_radgw/radius.c
@@ -0,0 +1,1373 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* freeDiameter author note:
+ *  The content from this file comes for the main part from the hostap project.
+ * It is redistributed under the terms of the BSD license, as allowed
+ * by the original copyright reproduced below.
+ * The modifications to this file are placed under the copyright of the freeDiameter project.
+ */
+
+/*
+ * 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.
+ */
+
+/*********************************************************************************/
+#include "rgw.h"
+
+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;
+}
+
+/* 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;
+}
+
+
+
+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 char print_char_buf[5];
+
+static char * print_char(char c)
+{
+	if (c >= 32 && c < 127)
+		sprintf(print_char_buf, "%c", c);
+	else
+		sprintf(print_char_buf, "<%02x>", c);
+	return print_char_buf;
+}
+
+
+static char * radius_msg_dump_attr_val(struct radius_attr_hdr *hdr, char * outbuf, size_t buflen)
+{
+	struct radius_attr_type *attr;
+	int i, len;
+	unsigned char *pos;
+	u8 attrtype;
+	
+	memset(outbuf, 0, buflen);
+
+	attr = radius_get_attr_type(hdr->type);
+
+	if (attr == NULL)
+		attrtype = RADIUS_ATTR_HEXDUMP;
+	else
+		attrtype = attr->data_type;
+
+	len = hdr->length - sizeof(struct radius_attr_hdr);
+	pos = (unsigned char *) (hdr + 1);
+
+	switch (attrtype) {
+	case RADIUS_ATTR_TEXT:
+		snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "      Value: '");
+		for (i = 0; i < len; i++)
+			snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "%s", print_char(pos[i]));
+		snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "'");
+		break;
+
+	case RADIUS_ATTR_IP:
+		if (len == 4) {
+			struct in_addr addr;
+			os_memcpy(&addr, pos, 4);
+			snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "      Value: %s", inet_ntoa(addr));
+		} else
+			snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "      Invalid IP address length %d", len);
+		break;
+
+	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));
+			snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "      Value: %s", atxt ? atxt : "?");
+		} else
+			snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "      Invalid IPv6 address length %d", len);
+		break;
+
+	case RADIUS_ATTR_INT32:
+		if (len == 4)
+			snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "      Value: %u", WPA_GET_BE32(pos));
+		else
+			snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "      Invalid INT32 length %d", len);
+		break;
+
+	case RADIUS_ATTR_HEXDUMP:
+	case RADIUS_ATTR_UNDIST:
+	default:
+		snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), "      Value:");
+		for (i = 0; i < len; i++)
+			snprintf(outbuf + strlen(outbuf), buflen - strlen(outbuf), " %02x", pos[i]);
+		break;
+	}
+	
+	return outbuf;
+}
+
+/* Dump a message  */
+void rgw_msg_dump(struct rgw_radius_msg_meta * msg, int has_meta)
+{
+	unsigned char *auth;
+	char buf[256];
+	size_t i;
+	if (! TRACE_BOOL(FULL) )
+		return;
+	
+	auth =  &(msg->radius.hdr->authenticator[0]);
+	
+	fd_log_debug("------ RADIUS msg dump -------");
+	fd_log_debug(" id  : 0x%02hhx, code : %hhd (%s), length : %d", msg->radius.hdr->identifier, msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code), ntohs(msg->radius.hdr->length));
+	fd_log_debug(" auth: %02hhx %02hhx %02hhx %02hhx  %02hhx %02hhx %02hhx %02hhx",
+			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",
+			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("    - Type: 0x%02hhx (%s)       Len: %-3hhu", attr->type, rgw_msg_attrtype_str(attr->type), attr->length);
+		fd_log_debug("%s", radius_msg_dump_attr_val(attr, buf, sizeof(buf)));
+	}
+	if (has_meta && msg->ps_nb) {
+		fd_log_debug("---- hidden attributes:");
+		for (i = msg->ps_first; i < msg->ps_first + msg->ps_nb; i++) {
+			struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[i]);
+			fd_log_debug("    - Type: 0x%02hhx (%s)       Len: %-3hhu", attr->type, rgw_msg_attrtype_str(attr->type), attr->length);
+			fd_log_debug("%s", radius_msg_dump_attr_val(attr, buf, sizeof(buf)));
+		}
+	}
+	fd_log_debug("-----------------------------");
+}
+
+
+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) {
+			fd_log_debug("WARNING: Could not add Message-Authenticator");
+			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) {
+		fd_log_debug("WARNING: too long RADIUS message (%lu)",
+		       (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];
+
+	if (msg->hdr->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
+	    os_memset(auth, 0, MD5_MAC_LEN);
+	    attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+				       auth, MD5_MAC_LEN);
+	    if (attr == NULL) {
+		    fd_log_debug("WARNING: Could not add Message-Authenticator");
+		    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));
+	} else {
+	    msg->hdr->length = htons(msg->buf_used);
+	}
+
+	/* 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) {
+		fd_log_debug("WARNING: too long RADIUS message (%lu)",
+		       (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) {
+		fd_log_debug("WARNING: too long RADIUS messages (%lu)",
+		       (unsigned long) msg->buf_used);
+	}
+}
+
+
+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) {
+		fd_log_debug("radius_msg_add_attr: too long attribute (%lu bytes)",
+		       (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;
+}
+
+
+/* Modified version of radius_msg_parse */
+int rgw_msg_parse(unsigned char * buf, size_t len, struct rgw_radius_msg_meta ** msg)
+{
+	struct rgw_radius_msg_meta * temp_msg = NULL;
+	struct radius_hdr *hdr;
+	struct radius_attr_hdr *attr;
+	size_t msg_len;
+	unsigned char *pos, *end;
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %zd %p", buf, len, msg);
+	
+	CHECK_PARAMS( buf && len >= sizeof(*hdr) && msg );
+	
+	*msg = NULL;
+	
+	/* Parse the RADIUS message */
+	hdr = (struct radius_hdr *) buf;
+	msg_len = ntohs(hdr->length);
+	if (msg_len < sizeof(*hdr) || msg_len > len) {
+		TRACE_DEBUG(INFO, "Invalid RADIUS message length");
+		return EINVAL;
+	}
+
+	if (msg_len < len) {
+		TRACE_DEBUG(INFO, "Ignored %lu extra bytes after RADIUS message",
+		       (unsigned long) len - msg_len);
+	}
+
+	CHECK_MALLOC( temp_msg = malloc(sizeof(struct rgw_radius_msg_meta)) );
+	memset(temp_msg, 0, sizeof(struct rgw_radius_msg_meta));
+	
+	if (radius_msg_initialize(&temp_msg->radius, msg_len)) {
+		TRACE_DEBUG(INFO, "Error in radius_msg_initialize, returning ENOMEM.");
+		free(temp_msg);
+		return ENOMEM;
+	}
+	
+	/* Store the received data in the alloc'd buffer */
+	memcpy(temp_msg->radius.buf, buf, msg_len);
+	temp_msg->radius.buf_size = temp_msg->radius.buf_used = msg_len;
+	
+	/* parse attributes */
+	pos = (unsigned char *) (temp_msg->radius.hdr + 1);
+	end = temp_msg->radius.buf + temp_msg->radius.buf_used;
+	
+	while (pos < end) {
+		if ((size_t) (end - pos) < sizeof(*attr)) {
+			TRACE_DEBUG(INFO, "Trucated attribute found in RADIUS buffer, EINVAL.");
+			ret = EINVAL;
+			break;
+		}
+			
+		attr = (struct radius_attr_hdr *) pos;
+	
+		if (pos + attr->length > end || attr->length < sizeof(*attr)) {
+			TRACE_DEBUG(INFO, "Trucated attribute found in RADIUS buffer, EINVAL.");
+			ret = EINVAL;
+			break;
+		}
+
+		if (radius_msg_add_attr_to_array(&temp_msg->radius, attr)) {
+			TRACE_DEBUG(INFO, "Error in radius_msg_add_attr_to_array, ENOMEM");
+			ret = ENOMEM;
+			break;
+		}
+		
+		if (attr->type == RADIUS_ATTR_PROXY_STATE)
+			temp_msg->ps_nb += 1;
+
+		pos += attr->length;
+	}
+	
+	if (ret != 0) {
+		radius_msg_free(&temp_msg->radius);
+		free(temp_msg);
+		return ret;
+	}
+	
+	/* Now move all the proxy-state attributes at the end of the attr_pos array */
+	if (temp_msg->ps_nb) {
+		size_t *temp_ps = NULL;
+		int n, new_n = 0, p = 0;
+		
+		CHECK_MALLOC( temp_ps = calloc(temp_msg->ps_nb, sizeof(size_t)) );
+		
+		/* Move all the Proxy-State attributes into the temp_ps array */
+		for (n=0; n < temp_msg->radius.attr_used; n++) {
+			struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(temp_msg->radius.buf + temp_msg->radius.attr_pos[n]);
+			
+			if (attr->type == RADIUS_ATTR_PROXY_STATE) {
+				temp_ps[p++] = temp_msg->radius.attr_pos[n];
+			} else {
+				temp_msg->radius.attr_pos[new_n++] = temp_msg->radius.attr_pos[n];
+			}
+		}
+		temp_msg->radius.attr_used = new_n; /* hide the proxy-state to other modules */
+		temp_msg->ps_first = new_n;
+		
+		/* And back into the array */
+		memcpy(temp_msg->radius.attr_pos + new_n, temp_ps, p * sizeof(size_t));
+		free(temp_ps);
+	}
+	
+	*msg = temp_msg;
+	return 0;
+}
+
+
+
+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) {
+				fd_log_debug("Multiple Message-Authenticator attributes in RADIUS message");
+				return 1;
+			}
+			attr = tmp;
+		}
+	}
+
+	if (attr == NULL) {
+		fd_log_debug("No Message-Authenticator attribute found");
+		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) {
+		fd_log_debug("Invalid Message-Authenticator!");
+		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) {
+		fd_log_debug("No matching Access-Request message found");
+		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) {
+		fd_log_debug("Response Authenticator invalid!");
+		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) {
+		fd_log_debug("Invalid ms key len %lu", (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) {
+		fd_log_debug("Failed to decrypt MPPE key");
+		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;
+}
diff --git a/extensions/app_radgw/radius.h b/extensions/app_radgw/radius.h
new file mode 100644
index 0000000..c278472
--- /dev/null
+++ b/extensions/app_radgw/radius.h
@@ -0,0 +1,331 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/*********************************************************************************/
+/* freeDiameter author note:
+ *  The content from this file comes mostly from the hostap project.
+ * It is redistributed under the terms of the BSD license, as allowed
+ * by the original copyright reproduced below.
+ * The changes to this file are placed under the copyright of the freeDiameter project.
+ */
+
+/*
+ * 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);
+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);
+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;
+}
+
+int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr *attr);
+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 */
diff --git a/extensions/app_radgw/rgw.h b/extensions/app_radgw/rgw.h
new file mode 100644
index 0000000..b6c0911
--- /dev/null
+++ b/extensions/app_radgw/rgw.h
@@ -0,0 +1,134 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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;
+	};
+	
+	/* For Proxy-State attributes: */
+	int	ps_first;	/* The index of the first Proxy-State attribute in radius.attr_pos. It is always >= radius.attr_used */
+	int	ps_nb;		/* The number of Proxy-State attributes. The real radius.attr_pos size is attr_used + ps_nb */
+};
+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 has_meta);
+
+/* 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 */
+enum rgw_cli_type { RGW_CLI_NAS, RGW_CLI_PXY };
+int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth);
+int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type );
+int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len);
+int rgw_clients_gettype(struct rgw_client * cli, enum rgw_cli_type *type);
+int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref);
+int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli);
+int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam);
+int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli);
+int rgw_client_finish_nosend(struct rgw_radius_msg_meta * req, struct rgw_client * cli);
+void rgw_clients_dispose(struct rgw_client ** ref);
+void rgw_clients_dump(void);
+int rgw_clients_init(void);
+void rgw_clients_fini(void);
+int rgw_client_session_add(struct rgw_client * cli, struct session *sess, char * dest_realm, char * dest_host, application_id_t appid);
+int rgw_client_session_stop(struct rgw_client * cli, struct session * sess, int32_t reason);
+
+
+/* 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 msg **diam_msg, struct rgw_client * cli);
+int rgw_plg_loop_ans(struct rgw_radius_msg_meta *req, 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 */
+  
diff --git a/extensions/app_radgw/rgw_clients.c b/extensions/app_radgw/rgw_clients.c
new file mode 100644
index 0000000..10922ce
--- /dev/null
+++ b/extensions/app_radgw/rgw_clients.c
@@ -0,0 +1,1181 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Manage the list of RADIUS clients, along with their shared secrets. */
+
+/* Probably some changes are needed to support RADIUS Proxies */
+
+#include "rgw.h"
+
+#define REVERSE_DNS_SIZE_MAX	512 /* length of our buffer for reverse DNS */
+#define DUPLICATE_CHECK_LIFETIME 60 /* number of seconds that the received RADIUS records are kept for duplicate checking . TODO: make it configurable if needed */
+
+/* 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);
+
+/* Lock to protect the previous lists. We use a rwlock because this list is mostly static, to allow parallel reading */
+static pthread_rwlock_t cli_rwl = PTHREAD_RWLOCK_INITIALIZER;
+
+/* Structure describing one received RADIUS message, for duplicate checks purpose. */
+struct req_info {
+	uint16_t	port; 	/* UDP source port of the request */
+	uint8_t		id;	/* The identifier in the request header */
+	uint8_t		auth[16]; /* Request authenticator, since some RADIUS clients do not implement the id mechanism properly. */
+	struct radius_msg *ans; /* The replied answer if any, in case the previous answer got lost. */
+	
+	int		nbdup;  /* Number of times this request was received as a duplicate */
+	struct fd_list	by_id;	  /* The list of requests ordered by their id, port, and auth */
+	time_t 		received; /* When was the last duplicate received? */
+	struct fd_list  by_time;  /* The list of requests ordered by the 'received' value . */
+};
+
+static pthread_t dbt_expire = (pthread_t)NULL; /* The thread that will remove old requests information from all clients (one thread for all) */
+
+/* Structure describing one client */
+struct rgw_client {
+	/* Link information in global list (cli_ip or cli_ip6) */
+	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 */
+	int			 is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */
+	enum rgw_cli_type 	 type; /* is it a proxy ? */
+	DiamId_t		 fqdn; /* malloc'd here */
+	size_t			 fqdn_len;
+	DiamId_t		 realm; /* references another string, do not free */
+	size_t			 realm_len;
+	struct {
+		os0_t		 name;
+		size_t		 len;
+	}			*aliases; /* Received 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 {
+		pthread_mutex_t dupl_lock;    /* The mutex protecting the following lists */
+		struct fd_list 	dupl_by_id;   /* The list of req_info structures ordered by their id, port, and auth */
+		struct fd_list 	dupl_by_time; /* The list of req_info structures ordered by their time (approximative) */
+	} dupl_info[2]; /*[0] for auth, [1] for acct. */
+};
+
+
+/* Create a new req_info structure and initialize its data from a RADIUS request message */
+static struct req_info * dupl_new_req_info(struct rgw_radius_msg_meta *msg) {
+	struct req_info * ret = NULL;
+	CHECK_MALLOC_DO( ret = malloc(sizeof(struct req_info)), return NULL );
+	memset(ret, 0, sizeof(struct req_info));
+	ret->port = msg->port;
+	ret->id   = msg->radius.hdr->identifier;
+	memcpy(&ret->auth[0], &msg->radius.hdr->authenticator[0], 16);
+	fd_list_init(&ret->by_id, ret);
+	fd_list_init(&ret->by_time, ret);
+	ret->received = time(NULL);
+	return ret;
+}
+
+/* Destroy a req_info structure, after it has been unlinked */
+static void dupl_free_req_info(struct req_info * r) {
+	CHECK_PARAMS_DO( r && FD_IS_LIST_EMPTY(&r->by_id) && FD_IS_LIST_EMPTY(&r->by_time), return );
+	if (r->ans) {
+		/* Free this RADIUS message */
+		radius_msg_free(r->ans);
+		free(r->ans);
+	}
+	
+	/* Use r->nbdup for some purpose? */
+	
+	free(r);
+}
+
+/* The core of the purge thread */
+static int dupl_purge_list(struct fd_list * clients) {
+
+	struct fd_list *li = NULL;
+	
+	for (li = clients->next; li != clients; li = li->next) {
+		struct rgw_client * client = (struct rgw_client *)li;
+		int p;
+		
+		for (p=0; p<=1; p++) {
+		
+			/* Lock this list */
+			time_t now;
+			CHECK_POSIX( pthread_mutex_lock(&client->dupl_info[p].dupl_lock) );
+			
+			now = time(NULL);
+			
+			while (!FD_IS_LIST_EMPTY(&client->dupl_info[p].dupl_by_time)) {
+			
+				/* Check the first item in the list */
+				struct req_info * r = (struct req_info *)(client->dupl_info[p].dupl_by_time.next->o);
+				
+				if (now - r->received > DUPLICATE_CHECK_LIFETIME) {
+				
+					TRACE_DEBUG(ANNOYING + 1, "Purging RADIUS request (id: %02hhx, port: %hu, dup #%d, age %ld secs)", r->id, ntohs(r->port), r->nbdup, (long)(now - r->received));
+					
+					/* Remove this record */
+					fd_list_unlink(&r->by_time);
+					fd_list_unlink(&r->by_id);
+					dupl_free_req_info(r);
+				} else {
+					/* We are done for this list */
+					break;
+				}
+			}
+			
+			CHECK_POSIX( pthread_mutex_unlock(&client->dupl_info[p].dupl_lock) );
+		}
+	}
+	return 0;
+}
+
+/* Thread that purges old RADIUS requests */
+static void * dupl_th(void * arg) {
+	/* Set the thread name */
+	fd_log_threadname ( "app_radgw:duplicate_purge" );
+	
+	/* The thread will be canceled */
+	while (1) {
+		
+		/* We don't use a cond var, we simply wake up every 5 seconds. If the size of the duplicate cache is critical, it might be changed */
+		sleep(5);
+		
+		/* When we wake up, we will check all clients duplicate lists one by one */
+		CHECK_POSIX_DO( pthread_rwlock_rdlock(&cli_rwl), break );
+		
+		CHECK_FCT_DO( dupl_purge_list(&cli_ip), break );
+		CHECK_FCT_DO( dupl_purge_list(&cli_ip6), break );
+		
+		CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), break );
+	
+		/* Loop */
+	}
+	
+	/* If we reach this part, some fatal error was encountered */
+	CHECK_FCT_DO(fd_core_shutdown(), );
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+}
+
+
+/* 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, enum rgw_cli_type type )
+{
+	struct rgw_client *tmp = NULL;
+	DiamId_t fqdn;
+	size_t fqdn_len = 0;
+	int ret, i;
+	int loc = 0;
+	
+	/* Check if the IP address is local */
+	if (   ( ((*ip_port)->sa_family == AF_INET ) && (   IN_IS_ADDR_LOOPBACK( &((struct sockaddr_in  *)(*ip_port))->sin_addr ) ) )
+	     ||( ((*ip_port)->sa_family == AF_INET6) && (  IN6_IS_ADDR_LOOPBACK( &((struct sockaddr_in6 *)(*ip_port))->sin6_addr) ) )) {
+		/* The client is local */
+		loc = 1;
+	} else {
+		char buf[255];
+	
+		/* 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;
+		}
+		fqdn = &buf[0];
+		CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&fqdn, &fqdn_len, 1),
+			{
+				TRACE_DEBUG(INFO, "Unable to use resolved peer name '%s' as DiameterIdentity: %s", buf, strerror(ret));
+				return ret;
+			} );
+	}
+	
+	/* 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);
+	
+	/* Initialize the duplicate list info */
+	for (i=0; i<=1; i++) {
+		CHECK_POSIX( pthread_mutex_init(&tmp->dupl_info[i].dupl_lock, NULL) );
+		fd_list_init(&tmp->dupl_info[i].dupl_by_id, NULL);
+		fd_list_init(&tmp->dupl_info[i].dupl_by_time, NULL);
+	}
+	tmp->type = type;
+	
+	if (loc) {
+		tmp->is_local = 1;
+	} else {
+		/* Copy the fqdn */
+		tmp->fqdn = fqdn;
+		tmp->fqdn_len = fqdn_len;
+
+		/* Find an appropriate realm */
+		tmp->realm = strchr(fqdn, '.');
+		if (tmp->realm) {
+			tmp->realm += 1;
+			tmp->realm_len = tmp->fqdn_len - (tmp->realm - fqdn);
+		}
+		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;
+			tmp->realm_len = fd_g_config->cnf_diamrlm_len;
+		}
+	}
+	
+	/* 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].name);
+		free(client->aliases);
+		free(client->fqdn);
+		free(client->sa);
+		free(client->key.data);
+		
+		/* Free the duplicate info */
+		for (idx=0; idx <= 1; idx++){
+			CHECK_POSIX_DO( pthread_mutex_lock( &client->dupl_info[idx].dupl_lock ), /* continue */ );
+			
+			while (!FD_IS_LIST_EMPTY(&client->dupl_info[idx].dupl_by_id)) {
+				struct req_info * r = (struct req_info *)(client->dupl_info[idx].dupl_by_id.next->o);
+				fd_list_unlink( &r->by_id );
+				fd_list_unlink( &r->by_time );
+				dupl_free_req_info(r);
+			}
+			
+			CHECK_POSIX_DO( pthread_mutex_unlock( &client->dupl_info[idx].dupl_lock ), /* continue */ );
+
+		}
+		
+		free(client);
+	}
+}
+
+
+/* Macro to avoid duplicating 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_rwl must be held for reading (at least) 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 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_gettype(struct rgw_client * cli, enum rgw_cli_type *type)
+{
+	CHECK_PARAMS( cli && type );
+	*type = cli->type;
+	return 0;
+}
+
+
+int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref)
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %p", ip_port, ref);
+	
+	CHECK_PARAMS(ip_port && ref);
+	
+	CHECK_POSIX( pthread_rwlock_rdlock(&cli_rwl) );
+
+	ret = client_search(ref, ip_port);
+	if (ret == EEXIST) {
+		(*ref)->refcount ++;
+		ret = 0;
+	} else {
+		*ref = NULL;
+	}
+	
+	CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) );
+	
+	return ret;
+}
+
+int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli)
+{
+	int p, dup = 0;
+	struct fd_list * li;
+	struct req_info * r;
+	
+	TRACE_ENTRY("%p %p", msg, cli);
+	
+	CHECK_PARAMS( msg && cli );
+	
+	if ((*msg)->serv_type == RGW_PLG_TYPE_AUTH)
+		p = 0;
+	else
+		p = 1;
+	
+	CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
+	
+	/* Search if we have this message in our list */
+	for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
+		int cmp = 0;
+		r = (struct req_info *)(li->o);
+		if (r->id < (*msg)->radius.hdr->identifier)
+			continue;
+		if (r->id > (*msg)->radius.hdr->identifier)
+			break;
+		if (r->port < (*msg)->port)
+			continue;
+		if (r->port > (*msg)->port)
+			break;
+		cmp = memcmp(&r->auth[0], &(*msg)->radius.hdr->authenticator[0], 16);
+		if (cmp < 0)
+			continue;
+		if (cmp > 0)
+			break;
+		dup = 1;
+		break;
+	}
+	
+	if (dup) {
+		time_t now = time(NULL);
+		r->nbdup += 1;
+		TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu, dup #%d, previously seen %ld secs ago).", 
+				r->id, ntohs(r->port), r->nbdup, (long)(now - r->received));
+		
+		if (r->ans) {
+			/* Resend the answer */
+			CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type, r->ans->buf, r->ans->buf_used, cli->sa, r->port),  );
+			
+			/* Should we delete 'r' so that a further duplicate will again be converted to Diameter? */
+		}
+		
+		/* Update the timestamp */
+		r->received = now;
+		fd_list_unlink(&r->by_time);
+		fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time); /* Move as last entry, since it is the most recent */
+		
+		/* Delete the request message */
+		rgw_msg_free(msg);
+		
+	} else {
+		/* The message was not a duplicate, we save it */
+		/* li currently points the the next entry in list_by_id */
+		CHECK_MALLOC_DO( r= dupl_new_req_info(*msg), { CHECK_POSIX_DO(pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ), ); return ENOMEM; } );
+		fd_list_insert_before(li, &r->by_id);
+		fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time); /* it is the most recent */
+	}
+		
+	CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
+	
+	return 0;
+}
+
+/* Check if the message has a valid authenticator, and update the meta-data accordingly */
+int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth)
+{
+	unsigned char * key;
+	size_t keylen;
+	int count;
+	
+	TRACE_ENTRY("%p %p %p", msg, cli, req_auth);
+	
+	CHECK_PARAMS(msg && cli);
+	
+	CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen));
+	
+	count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0);
+	if (count > 1) {
+		TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count);
+		return EINVAL;
+	}
+	if (count == 0) {
+		TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes.");
+		msg->valid_mac = 0;
+	} else {
+		if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) {
+			TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message.");
+			return EINVAL;
+		}
+		msg->valid_mac = 1;
+	}
+	
+	return 0;
+}
+
+static struct dict_object * cache_orig_host = NULL;
+static struct dict_object * cache_orig_realm = NULL;
+static struct dict_object * cache_route_record = NULL;
+
+int rgw_clients_init(void)
+{
+	TRACE_ENTRY();
+	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) );
+	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) );
+	CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &cache_route_record, ENOENT) );
+	
+	/* Create the thread that will purge old RADIUS duplicates */
+	CHECK_POSIX( pthread_create( &dbt_expire, NULL, dupl_th, NULL) );
+	
+	return 0;
+}
+
+
+/* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter
+ message with the appropriate routing information */
+/* 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 */
+int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam)
+{
+	int idx;
+	int valid_nas_info = 0;
+	struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL;
+	size_t nas_id_len;
+	char * oh_str = NULL; size_t oh_strlen = 0; int oh_free = 0;
+	char * or_str = NULL; size_t or_strlen = 0;
+	char * rr_str = NULL; size_t rr_strlen = 0;
+	char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */
+	
+	struct avp *avp = NULL;
+	union avp_value avp_val;
+	
+	TRACE_ENTRY("%p %p %p", msg, cli, diam);
+	CHECK_PARAMS(msg && cli && diam && (*diam == NULL));
+	
+	/* Find the relevant attributes, if any */
+	for (idx = 0; idx < msg->radius.attr_used; idx++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]);
+		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;
+			nas_id_len = attr_len;
+			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.");
+		
+		/* Get information on this peer */
+		CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+		
+		goto diameter;
+	}
+	
+	/* Check if the message was received from the IP in NAS-IP-Address attribute */
+	if (nas_ip && (cli->sa->sa_family == AF_INET) && !memcmp(nas_ip+1, &cli->sin->sin_addr, sizeof(struct in_addr))) {
+		TRACE_DEBUG(FULL, "NAS-IP-Address contains the same address as the message was received from.");
+		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.");
+		valid_nas_info |= 2;
+	}
+	
+	
+	/*
+			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.
+	*/
+	if (nas_ip || nas_ip6) {
+		if (!valid_nas_info) {
+			if ((!cli->is_local) && (cli->type == RGW_CLI_NAS)) {
+				TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different from the sender's. Please configure as Proxy if this is expected. Message discarded.");
+				return EINVAL;
+			} else {
+				int ret;
+				sSS ss;
+				/* the peer is configured as a proxy, or running on localhost, so accept the message */
+				
+				/* In that case, the cli will be stored as Route-Record and the NAS-IP-Address as origin */
+				if (!cli->is_local) {
+					rr_str = cli->fqdn;
+					rr_strlen = cli->fqdn_len;
+				}
+				
+				/* We must DNS-reverse the NAS-IP*-Address */
+				memset(&ss, 0 , sizeof(sSS));
+				if (nas_ip) {
+					sSA4 * sin = (sSA4 *)&ss;
+					sin->sin_family = AF_INET;
+					memcpy(&sin->sin_addr, nas_ip + 1, sizeof(struct in_addr));
+				} else {
+					sSA6 * sin6 = (sSA6 *)&ss;
+					sin6->sin6_family = AF_INET6;
+					memcpy(&sin6->sin6_addr, nas_ip6 + 1, sizeof(struct in6_addr));
+				}
+				CHECK_SYS_DO( getnameinfo( (sSA *)&ss, sSAlen(&ss), &buf[0], sizeof(buf), NULL, 0, NI_NAMEREQD),
+					{
+						if (cli->is_local) {
+							CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+							goto diameter;
+						}
+						
+						TRACE_DEBUG(INFO, "The NAS-IP*-Address cannot be DNS reversed in order to create the Origin-Host AVP; rejecting the message (translation is impossible).");
+						return EINVAL;
+					} );
+					
+				oh_str = &buf[0];
+				CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1),
+					{
+						if (cli->is_local) {
+							CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+							goto diameter;
+						}
+						
+						TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret));
+						return ret;
+					} );
+				oh_free = 1;
+				
+				or_str = strchr(oh_str, '.');
+				if (or_str) {
+					or_str ++; /* move after the first dot */
+					if (*or_str == '\0')
+						or_str = NULL; /* Discard this realm, we will use the local realm later */
+					else
+						or_strlen = oh_strlen - (or_str - oh_str);
+				}
+			}
+		} else {
+			/* The attribute matches the source address, just use this in origin-host */
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+		}
+		
+		goto diameter; /* we ignore the nas_id in that case */
+	}
+	
+	/* We don't have a NAS-IP*-Address attribute if we are here */
+	if (cli->is_local) {
+		/* Simple: we use our own configuration */
+		CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+		goto diameter;
+	}
+	
+	/* At this point, we only have nas_id, and the client is not local */
+	ASSERT(nas_id);
+	
+	{
+		int found, ret;
+		struct addrinfo hint, *res, *ptr;
+		
+		/*
+			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.
+		*/
+		
+		/* first, check if the nas_id is the fqdn of the peer or a known alias */
+		if (!fd_os_almostcasesrch(nas_id + 1, nas_id_len, 
+						cli->fqdn, cli->fqdn_len, NULL)) {
+			TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the client");
+			found = 1;
+		} else {
+			for (idx = 0; idx < cli->aliases_nb; idx++) {
+				if (!fd_os_cmp(nas_id + 1, nas_id_len, 
+						cli->aliases[idx].name, cli->aliases[idx].len)) {
+					TRACE_DEBUG(FULL, "NAS-Identifier valid value found in the cache");
+					found = 1;
+					break;
+				}
+			}
+		}
+		
+		if (found) {
+			/* The NAS-Identifier matches the source IP */
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+
+			goto diameter;
+		}
+		
+		/* Attempt DNS resolution of the identifier */
+		ASSERT( nas_id_len < sizeof(buf) );
+		memcpy(buf, nas_id + 1, nas_id_len);
+		buf[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0';
+		
+		/* Now check if this alias is valid for this peer */
+		memset(&hint, 0, sizeof(hint));
+		hint.ai_flags  = AI_CANONNAME;
+		ret = getaddrinfo(buf, NULL, &hint, &res);
+		if (ret == 0) {
+			strncpy(buf, res->ai_canonname, sizeof(buf));
+			/* The name was resolved correctly, does it match the IP of the client? */
+			for (ptr = res; ptr != NULL; ptr = ptr->ai_next) {
+				if (cli->sa->sa_family != ptr->ai_family)
+					continue;
+				if (memcmp(cli->sa, ptr->ai_addr, sSAlen(cli->sa)))
+					continue;
+				
+				found = 1;
+				break;
+			}
+			freeaddrinfo(res);
+			
+			if (!found) {
+				if (cli->type == RGW_CLI_NAS) {
+					TRACE_DEBUG(INFO, "The NAS-Identifier value '%.*s' resolves to a different IP than the client's, discarding the message. Configure this client as a Proxy if this message should be valid.", 
+						(int)nas_id_len, (char *)(nas_id + 1));
+					return EINVAL;
+				} else {
+					/* This identifier matches a different IP, assume it is a proxied message */
+					if (!cli->is_local) {
+						rr_str = cli->fqdn;
+						rr_strlen = cli->fqdn_len;
+					}
+					oh_str = &buf[0]; /* The canonname resolved */
+					oh_strlen = 0;
+					CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1),
+						{
+							TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret));
+							return ret;
+						} );
+					oh_free = 1;
+					or_str = strchr(oh_str, '.');
+					if (or_str) {
+						or_str ++; /* move after the first dot */
+						if (*or_str == '\0')
+							or_str = NULL; /* Discard this realm, we will use the local realm later */
+						else
+							or_strlen = oh_strlen - (or_str - oh_str);
+					}
+				}
+			} else {
+				/* It is a valid alias, save it */
+				CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(cli->aliases[0])) );
+				
+				CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1].name = os0dup(nas_id + 1, nas_id_len ) );
+				cli->aliases[cli->aliases_nb + 1].len = nas_id_len;
+
+				cli->aliases_nb ++;
+				TRACE_DEBUG(FULL, "Saved valid alias for client: '%.*s' -> '%s'", (int)nas_id_len, (char *)(nas_id + 1), cli->fqdn);
+				CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+			}
+		} else {
+			/* Error resolving the name */
+			TRACE_DEBUG(INFO, "NAS-Identifier '%s' cannot be resolved: %s. Ignoring...", buf, gai_strerror(ret));
+			/* Assume this is a valid identifier for the client */
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+		}
+	}
+	
+	/* Now, let's create the empty Diameter message with Origin-Host, -Realm, and Route-Record if needed. */
+diameter:
+	ASSERT(oh_str); /* If it is not defined here, there is a bug... */
+	if (!or_str) {
+		or_str = fd_g_config->cnf_diamrlm; /* Use local realm in that case */
+		or_strlen = fd_g_config->cnf_diamrlm_len;
+	}
+	
+	/* Create an empty Diameter message so that extensions can store their AVPs */
+	CHECK_FCT(  fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam )  );
+	
+	/* Add the Origin-Host as next AVP */
+	CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) );
+	memset(&avp_val, 0, sizeof(avp_val));
+	avp_val.os.data = (unsigned char *)oh_str;
+	avp_val.os.len = oh_strlen;
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
+	
+	/* Add the Origin-Realm as next AVP */
+	CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) );
+	memset(&avp_val, 0, sizeof(avp_val));
+	avp_val.os.data = (unsigned char *)or_str;
+	avp_val.os.len = or_strlen;
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
+	
+	if (rr_str) {
+		CHECK_FCT( fd_msg_avp_new ( cache_route_record, 0, &avp ) );
+		memset(&avp_val, 0, sizeof(avp_val));
+		avp_val.os.data = (unsigned char *)rr_str;
+		avp_val.os.len = rr_strlen;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	if (oh_free)
+		free(oh_str);
+	
+	/* Done! */
+	return 0;
+}
+
+int rgw_clients_get_origin(struct rgw_client *cli, DiamId_t *fqdn, size_t *fqdnlen, DiamId_t *realm, size_t *realmlen)
+{
+	TRACE_ENTRY("%p %p %p %p %p", cli, fqdn, fqdnlen, realm, realmlen);
+	CHECK_PARAMS(cli && fqdn && fqdnlen);
+	
+	if (cli->is_local) {
+		*fqdn = fd_g_config->cnf_diamid;
+		*fqdnlen = fd_g_config->cnf_diamid_len;
+		if (realm)
+			*realm= fd_g_config->cnf_diamrlm;
+		if (realmlen)
+			*realmlen= fd_g_config->cnf_diamrlm_len;
+	} else {
+		*fqdn = cli->fqdn;
+		*fqdnlen = cli->fqdn_len;
+		if (realm)
+			*realm= cli->realm;
+		if (realmlen)
+			*realmlen= cli->realm_len;
+	}
+		
+	return 0;
+}
+
+char * rgw_clients_id(struct rgw_client *cli)
+{
+	return cli->is_local ? "(local)" : cli->fqdn;
+}
+
+
+void rgw_clients_dispose(struct rgw_client ** ref)
+{
+	TRACE_ENTRY("%p", ref);
+	CHECK_PARAMS_DO(ref, return);
+	
+	CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl),  );
+	client_unlink(*ref);
+	*ref = NULL;
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), );
+}
+
+int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
+{
+	struct rgw_client * prev = NULL, *new = NULL;
+	int ret;
+	
+	TRACE_ENTRY("%p %p %zu", 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) );
+	CHECK_PARAMS( (type == RGW_CLI_NAS) || (type == RGW_CLI_PXY) );
+	
+	/* Dump the entry in debug mode */
+	if (TRACE_BOOL(FULL + 1 )) {
+		char sa_buf[sSA_DUMP_STRLEN];
+		fd_sa_sdump_numeric(sa_buf, ip_port);
+		TRACE_DEBUG(FULL, "Adding %s:", (type == RGW_CLI_NAS) ? "NAS" : "PROXY"  );
+		TRACE_DEBUG(FULL, "\tIP : %s", sa_buf );
+		TRACE_BUFFER(FD_LOG_DEBUG, FULL, "\tKey: [", *key, keylen, "]" );
+	}
+	
+	/* Lock the lists */
+	CHECK_POSIX( pthread_rwlock_wrlock(&cli_rwl) );
+	
+	/* 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, type ), goto end );
+		fd_list_insert_after(&prev->chain, &new->chain);
+		new->refcount++;
+		ret = 0;
+		goto end;
+	}
+	
+	if (ret == EEXIST) {
+		char sa_buf[sSA_DUMP_STRLEN];
+		/* Check if the key is the same, then skip or return an error */
+		if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) ) && (type == prev->type)) {
+			TRACE_DEBUG(INFO, "Skipping duplicate client description");
+			ret = 0;
+			goto end;
+		}
+		
+		fd_log_error("ERROR: Conflicting RADIUS clients descriptions!");
+		TRACE_ERROR("Previous entry: %s", (prev->type == RGW_CLI_NAS) ? "NAS" : "PROXY");
+		fd_sa_sdump_numeric(sa_buf, prev->sa);
+		TRACE_ERROR("\tIP : %s", sa_buf);
+		TRACE_BUFFER(FD_LOG_ERROR, NONE, "\tKey: [", prev->key.data, prev->key.len, "]" );
+		TRACE_ERROR("Conflicting entry: %s", (type == RGW_CLI_NAS) ? "NAS" : "PROXY");
+		fd_sa_sdump_numeric(sa_buf, ip_port);
+		TRACE_ERROR("\tIP : %s", sa_buf);
+		TRACE_BUFFER(FD_LOG_ERROR, NONE, "\tKey: [", *key, keylen, "]" );
+	}
+end:
+	/* release the lists */
+	CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) );
+	
+	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) {
+		char sa_buf[sSA_DUMP_STRLEN];
+		client = (struct rgw_client *)ref;
+		fd_sa_sdump_numeric(sa_buf, client->sa);
+		LOG_D("  - %s%s", sa_buf, (client->type == RGW_CLI_NAS) ? "" : " [PROXY]" );
+	}
+}
+
+void rgw_clients_dump(void)
+{
+	if ( ! TRACE_BOOL(FULL) )
+		return;
+	
+	CHECK_POSIX_DO( pthread_rwlock_rdlock(&cli_rwl), /* ignore error */ );
+	
+	if (!FD_IS_LIST_EMPTY(&cli_ip))
+		fd_log_debug(" RADIUS IP clients list:");
+	dump_cli_list(&cli_ip);
+		
+	if (!FD_IS_LIST_EMPTY(&cli_ip6))
+		fd_log_debug(" RADIUS IPv6 clients list:");
+	dump_cli_list(&cli_ip6);
+		
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), /* ignore error */ );
+}
+
+void rgw_clients_fini(void)
+{
+	struct fd_list * client;
+	
+	TRACE_ENTRY();
+	
+	CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl), /* ignore error */ );
+	
+	CHECK_FCT_DO( fd_thr_term(&dbt_expire), /* continue */ );
+
+	/* 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_rwlock_unlock(&cli_rwl), /* ignore error */ );
+	
+}
+
+int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli)
+{
+	int p;
+	struct fd_list * li;
+	
+	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;
+	}
+	
+	/* Add all the Proxy-States back in the message */
+	for (p = 0; p < req->ps_nb; p++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(req->radius.buf + req->radius.attr_pos[req->ps_first + p]);
+		
+		if (radius_msg_add_attr_to_array(*msg, attr)) {
+			TRACE_DEBUG(INFO, "Error in radius_msg_add_attr_to_array, ENOMEM");
+			radius_msg_free(*msg);
+			free(*msg);
+			*msg = NULL;
+			return ENOMEM;
+		}
+	}
+	
+	/* Add the Message-Authenticator if needed, and other final tasks */
+	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, 0);
+
+	/* Send the message */
+	CHECK_FCT( rgw_servers_send(req->serv_type, (*msg)->buf, (*msg)->buf_used, cli->sa, req->port) );
+
+	/* update the duplicate cache */
+	if (req->serv_type == RGW_PLG_TYPE_AUTH)
+		p = 0;
+	else
+		p = 1;
+	
+	CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
+	
+	/* Search this message in our list */
+	for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
+		int cmp = 0;
+		struct req_info * r = (struct req_info *)(li->o);
+		if (r->id < req->radius.hdr->identifier)
+			continue;
+		if (r->id > req->radius.hdr->identifier)
+			break;
+		if (r->port < req->port)
+			continue;
+		if (r->port > req->port)
+			break;
+		cmp = memcmp(&r->auth[0], &req->radius.hdr->authenticator[0], 16);
+		if (cmp < 0)
+			continue;
+		if (cmp > 0)
+			break;
+		
+		/* We have the request in our duplicate cache */
+		/* This should not happen, but just in case... */
+		if (r->ans) {
+			radius_msg_free(r->ans);
+			free(r->ans);
+		}
+		
+		/* Now save the message */
+		r->ans = *msg;
+		*msg = NULL;
+		
+		/* Update the timestamp */
+		{
+			time_t now = time(NULL);
+			r->received = now;
+			fd_list_unlink(&r->by_time); /* Move as last entry, since it is the most recent */
+			fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time);
+		}
+		break;
+	}
+		
+	CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
+	
+	/* If we have not found the request in our list, the purge time is probably too small */
+	if (*msg) {
+		TODO("Augment the purge time...");
+		/* If we receive the duplicate request again, it will be converted to Diameter... */
+		radius_msg_free(*msg);
+		free(*msg);
+		*msg = NULL;
+	}
+	
+	/* Finished */
+	return 0;
+}
+
+/* Call this function when a RADIUS request has explicitely no answer (mainly accounting) so 
+that we purge the duplicate cache and allow further message to be translated again.
+This is useful for example when a temporary error occurred in Diameter (like UNABLE_TO_DELIVER) */
+int rgw_client_finish_nosend(struct rgw_radius_msg_meta * req, struct rgw_client * cli)
+{
+	int p;
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %p", req, cli);
+	CHECK_PARAMS( req && cli );
+	
+	/* update the duplicate cache */
+	if (req->serv_type == RGW_PLG_TYPE_AUTH)
+		p = 0;
+	else
+		p = 1;
+	
+	CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
+	
+	/* Search this message in our list */
+	for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
+		int cmp = 0;
+		struct req_info * r = (struct req_info *)(li->o);
+		if (r->id < req->radius.hdr->identifier)
+			continue;
+		if (r->id > req->radius.hdr->identifier)
+			break;
+		if (r->port < req->port)
+			continue;
+		if (r->port > req->port)
+			break;
+		cmp = memcmp(&r->auth[0], &req->radius.hdr->authenticator[0], 16);
+		if (cmp < 0)
+			continue;
+		if (cmp > 0)
+			break;
+		
+		/* We have the request in our duplicate cache, remove it */
+		fd_list_unlink(&r->by_id);
+		fd_list_unlink(&r->by_time);
+		dupl_free_req_info(r);
+		break;
+	}
+		
+	CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
+	
+	/* Finished */
+	return 0;
+}
+
diff --git a/extensions/app_radgw/rgw_common.h b/extensions/app_radgw/rgw_common.h
new file mode 100644
index 0000000..bfb700a
--- /dev/null
+++ b/extensions/app_radgw/rgw_common.h
@@ -0,0 +1,287 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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;
+
+int    rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len);
+char * rgw_clients_id(struct rgw_client *cli);
+int    rgw_clients_get_origin(struct rgw_client *cli, DiamId_t *fqdn, size_t *fqdnlen, DiamId_t *realm, size_t *realmlen);
+
+/* Each plugin must provide the following structure. */
+extern struct rgw_api {
+	/* The name of the plugin */
+	const char * rgwp_name;
+
+	/* Parse the configuration file. It may be called several times with different configurations.
+	    Called even if no configuration file is passed (with NULL conf_file parameter then) */
+	int (*rgwp_conf_parse) ( char * conf_file, struct rgwp_config ** state );
+	
+	/* Cleanup the configuration state when the daemon is exiting (called even if state is NULL). */
+	void (*rgwp_conf_free) (struct rgwp_config * state);
+
+	/* handle an incoming RADIUS message */
+	int	(*rgwp_rad_req) ( struct rgwp_config * conf, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli );
+	/* ret >0: critical error (errno), log and exit.
+	   ret 0: continue; 
+	   ret -1: stop processing this message
+	   ret -2: reply the content of rad_ans to the RADIUS client immediatly
+	 */
+	
+	/* handle the corresponding Diameter answer */
+	int	(*rgwp_diam_ans) ( struct rgwp_config * conf, 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
+
+/* Class attribute prefix to store the Auth Application Id (required to send STR) */
+#define CLASS_AAI_PREFIX 	"fD/rgwx/aai:"
+
+/* 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_ASSIGNMENT_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,
+	RADIUS_ATTR_DIGEST_RESPONSE = 103,
+	RADIUS_ATTR_DIGEST_REALM = 104,
+	RADIUS_ATTR_DIGEST_NONCE = 105,
+	RADIUS_ATTR_DIGEST_RESPONSE_AUTH=106,
+	RADIUS_ATTR_DIGEST_NEXTNONCE = 107,
+	RADIUS_ATTR_DIGEST_METHOD = 108,
+	RADIUS_ATTR_DIGEST_URI = 109,
+	RADIUS_ATTR_DIGEST_QOP = 110,
+	RADIUS_ATTR_DIGEST_ALGORITHM = 111,
+	RADIUS_ATTR_DIGEST_ENTITY_BODY_HASH=112,
+	RADIUS_ATTR_DIGEST_CNONCE = 113,
+	RADIUS_ATTR_DIGEST_NONCE_COUNT = 114,
+	RADIUS_ATTR_DIGEST_USERNAME = 115,
+	RADIUS_ATTR_DIGEST_HA1 = 121,
+	RADIUS_ATTR_SIP_AOR = 122
+};
+
+enum {  DIAM_ATTR_USER_NAME = 1,
+	DIAM_ATTR_USER_PASSWORD = 2,
+	DIAM_ATTR_NAS_IP_ADDRESS = 4,
+	DIAM_ATTR_NAS_PORT = 5,
+	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_NAS_IDENTIFIER = 32,
+	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_ACCT_SESSION_ID = 44,
+	DIAM_ATTR_ACCT_MULTI_SESSION_ID = 50,
+	DIAM_ATTR_EVENT_TIMESTAMP = 55,
+	DIAM_ATTR_NAS_PORT_TYPE = 61,
+	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_ASSIGNMENT_ID = 82,
+	DIAM_ATTR_TUNNEL_PREFERENCE = 83,
+	DIAM_ATTR_ARAP_CHALLENGE_RESPONSE = 84,
+	DIAM_ATTR_ACCT_INTERIM_INTERVAL = 85,
+	DIAM_ATTR_NAS_PORT_ID = 87,
+	DIAM_ATTR_FRAMED_POOL = 88,
+	DIAM_ATTR_TUNNEL_CLIENT_AUTH_ID = 90,
+	DIAM_ATTR_TUNNEL_SERVER_AUTH_ID = 91,
+	DIAM_ATTR_NAS_IPV6_ADDRESS = 95,
+	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_DIGEST_RESPONSE = 103,
+	DIAM_ATTR_DIGEST_REALM = 104,
+	DIAM_ATTR_DIGEST_NONCE = 105,
+	DIAM_ATTR_DIGEST_RESPONSE_AUTH=106,
+	DIAM_ATTR_DIGEST_NEXTNONCE = 107,
+	DIAM_ATTR_DIGEST_METHOD = 108,
+	DIAM_ATTR_DIGEST_URI = 109,
+	DIAM_ATTR_DIGEST_QOP = 110,
+	DIAM_ATTR_DIGEST_ALGORITHM = 111,
+	DIAM_ATTR_DIGEST_ENTITY_BODY_HASH=112,
+	DIAM_ATTR_DIGEST_CNONCE = 113,
+	DIAM_ATTR_DIGEST_NONCE_COUNT = 114,
+	DIAM_ATTR_DIGEST_USERNAME = 115,
+	DIAM_ATTR_DIGEST_HA1 = 121,
+	DIAM_ATTR_SIP_AOR = 122,
+	DIAM_ATTR_AUTH_APPLICATION_ID = 258,
+	DIAM_ATTR_ACCT_APPLICATION_ID = 259,
+	DIAM_ATTR_VENDOR_SPECIFIC_APPLICATION_ID = 260,
+	DIAM_ATTR_SESSION_ID = 263,
+	DIAM_ATTR_ORIGIN_HOST = 264,
+	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_ACCOUNTING_SUB_SESSION_ID = 287,
+	DIAM_ATTR_ERROR_REPORTING_HOST = 294,
+	DIAM_ATTR_TERMINATION_CAUSE = 295,
+	DIAM_ATTR_ORIGIN_REALM = 296,
+	DIAM_ATTR_SIP_AUTH_DATA_ITEM = 376,
+	DIAM_ATTR_SIP_AUTHENTICATION_SCHEME = 377,
+	DIAM_ATTR_SIP_AUTHENTICATE = 379,
+	DIAM_ATTR_SIP_NUMBER_AUTH_ITEMS = 382,
+	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,
+	DIAM_ATTR_ACCOUNTING_RECORD_TYPE = 480,
+	DIAM_ATTR_ACCOUNTING_REALTIME_REQUIRED = 483,
+	DIAM_ATTR_ACCOUNTING_RECORD_NUMBER = 485
+};
+
+const char * rgw_msg_attrtype_str(unsigned char c);
+const char * rgw_msg_code_str(unsigned char c);
+
+#endif /* _RGW_COMMON_H */
+  
diff --git a/extensions/app_radgw/rgw_conf.l b/extensions/app_radgw/rgw_conf.l
new file mode 100644
index 0000000..8afd779
--- /dev/null
+++ b/extensions/app_radgw/rgw_conf.l
@@ -0,0 +1,277 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+/* 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", 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", 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:"nas"|"cli")	{ BEGIN(IN_CLI1); yylval->integer=RGW_CLI_NAS; return NAS_OR_PXY; 		}
+(?i:"pxy")		{ BEGIN(IN_CLI1); yylval->integer=RGW_CLI_PXY; return NAS_OR_PXY; 		}
+
+	/* Match an IP (4 or 6) and optional port */
+<IN_CLI1>({IP4}|{IP6}){BR_PORT}?	{
+				char * work;
+				char * port;
+				unsigned short p = 0;
+				
+				work = strdup(yytext);
+				if ( work == NULL ) {
+					fd_log_debug("Unable to allocate memory: %s", strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+				}
+				
+				if ((port = strchr(work, '[')) != NULL) {
+					*port = '\0';
+					port++;
+					if (sscanf(port, "%hu]", &p) != 1) {
+						fd_log_debug("'%s' is not a valid port: %s", 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", 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", 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", 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", 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")	{ BEGIN(EXPECT_DECINT); 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")	{ BEGIN(EXPECT_DECINT); 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", 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", 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", 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", 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", 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'.", yylloc->first_line, yylloc->first_column, yytext);
+			 	return LEX_ERROR; 
+			}
+
+%%
diff --git a/extensions/app_radgw/rgw_conf.y b/extensions/app_radgw/rgw_conf.y
new file mode 100644
index 0000000..53ab746
--- /dev/null
+++ b/extensions/app_radgw/rgw_conf.y
@@ -0,0 +1,404 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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/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", 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", 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", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
+	else
+		fd_log_debug("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s);
+}
+
+/* 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", 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>	FINDFILEEXT
+
+%token <integer> NAS_OR_PXY
+
+/* simple tokens */
+%token		DISABLED
+%token		AUTH
+%token		ACCT
+
+%token 		PLG_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
+			;
+
+				
+/* -------------------------------------- */
+FINDFILEEXT:		QSTRING
+			{
+				char * fname = $1;
+				FILE * fd;
+				
+				/* First, check if the file exists */
+				fd = fopen(fname, "r");
+				if ((fd == NULL) && (*fname != '/')) {
+					char * bkp = fname;
+					CHECK_MALLOC_DO( fname = malloc( strlen(bkp) + strlen(DEFAULT_EXTENSIONS_PATH) + 2 ),
+						{ yyerror (&yylloc, conffile, "Not enough memory"); YYERROR; } );
+					sprintf(fname, DEFAULT_EXTENSIONS_PATH "/%s", bkp);
+					free(bkp);
+					fd = fopen(fname, "r");
+				}
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_DEBUG(INFO, "Unable to open file %s for reading: %s", fname, strerror(ret));
+					yyerror (&yylloc, conffile, "Error adding plugin"); 
+					YYERROR;
+				}
+				fclose(fd);
+				
+				$$ = fname;
+			}
+			;
+/* -------------------------------------- */
+plugin:			{
+				/* Reset the parameters */
+				buf_reinit();
+				port = RGW_PLG_TYPE_AUTH | RGW_PLG_TYPE_ACCT ;
+				free(plgconffile); plgconffile = NULL;
+			}
+			PLG_PREFIX '=' FINDFILEEXT 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();
+			}
+			NAS_OR_PXY '=' IP '/' clisecret_key ';'
+			{
+				/* Add this client */
+				if ( rgw_clients_add( $4, &buf, buf_sz, $2 ) ) {
+					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;
+			}
+			;
diff --git a/extensions/app_radgw/rgw_main.c b/extensions/app_radgw/rgw_main.c
new file mode 100644
index 0000000..9276ef1
--- /dev/null
+++ b/extensions/app_radgw/rgw_main.c
@@ -0,0 +1,76 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+/* 
+ * 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_clients_init() );
+	
+	CHECK_FCT( rgw_servers_init() );
+	
+	CHECK_FCT( rgw_conf_handle(conffile) );
+	
+	LOG_D( "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);
diff --git a/extensions/app_radgw/rgw_msg_attrtype.c b/extensions/app_radgw/rgw_msg_attrtype.c
new file mode 100644
index 0000000..b14e54c
--- /dev/null
+++ b/extensions/app_radgw/rgw_msg_attrtype.c
@@ -0,0 +1,300 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, 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.								 *
+*********************************************************************************************************/
+#include "rgw_common.h"
+
+/* The content of this file was semi-automatically generated from the IANA registry. */
+
+/*  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)) return "Reserved [RFC3575]";
+	/* fallback */ return "[Unknown]";
+}
diff --git a/extensions/app_radgw/rgw_msg_codes.c b/extensions/app_radgw/rgw_msg_codes.c
new file mode 100644
index 0000000..19b3e72
--- /dev/null
+++ b/extensions/app_radgw/rgw_msg_codes.c
@@ -0,0 +1,120 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, 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.								 *
+*********************************************************************************************************/
+#include "rgw_common.h"
+
+/* The content of this file was semi-automatically generated from the IANA registry. */
+
+/*  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]";
+}
diff --git a/extensions/app_radgw/rgw_plugins.c b/extensions/app_radgw/rgw_plugins.c
new file mode 100644
index 0000000..3bd4526
--- /dev/null
+++ b/extensions/app_radgw/rgw_plugins.c
@@ -0,0 +1,491 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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 */
+};
+
+/* 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;
+	
+	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( new = malloc(sizeof(struct plg_descr)) );
+	memset(new, 0, sizeof(struct plg_descr));
+	
+	fd_list_init(&new->chain, new);
+	
+	/* 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: %s", 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': %s", plgfile, dlerror());
+		goto error;
+	}
+	
+	TRACE_DEBUG(FULL, "Plugin '%s' found in file '%s'", new->descriptor->rgwp_name, plgfile);
+	
+	/* Now parse the configuration file, this will initialize all plugin states and store it in the "cs" pointer (the plugin must be re-entrant, so no global state) */
+	if (new->descriptor->rgwp_conf_parse) {
+		CHECK_FCT_DO( (*(new->descriptor->rgwp_conf_parse))(conffile, &new->cs), 
+			{
+				fd_log_debug("An error occurred while parsing configuration file '%s' in plugin '%s', aborting...", conffile, plgfile);
+				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:");
+	for (ptr = plg_list.next; ptr != &plg_list; ptr = ptr->next) {
+		char buf[1024];
+		plg = (struct plg_descr *)ptr;
+		
+		snprintf(buf, sizeof(buf), "  %-25s ( %p ) - types: %s%s, codes: ", 
+			 plg->descriptor->rgwp_name, 
+			 plg->cs,
+			 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++) {
+				snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%02hhx ", plg->cc[i]);
+			}
+			fd_log_debug("%s", buf);
+		} else {
+			fd_log_debug("%s*", buf);
+		}
+	}
+	
+	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:");
+	
+	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:", 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 (%p)", item->plg->descriptor->rgwp_name, item->plg->cs);
+		}
+	}
+	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:", 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 (%p)", item->plg->descriptor->rgwp_name, item->plg->cs);
+		}
+	}
+	
+	
+	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 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", rad, diam_msg, cli);
+	CHECK_PARAMS( rad && *rad && 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->descriptor->rgwp_name);
+			ret = (*plg->descriptor->rgwp_rad_req)(plg->cs, &(*rad)->radius, &rad_ans, diam_msg, cli);
+			if (ret)
+				break;
+		} else {
+			TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", plg->descriptor->rgwp_name);
+		}					
+	}
+	
+	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;
+	}
+	
+	/* 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 */
+		TRACE_DEBUG(NONE, "An error occurred while handling a RADIUS message from '%s': %s", rgw_clients_id(cli), 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 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", req, diam_ans, rad_ans, cli);
+	CHECK_PARAMS( req && 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->descriptor->rgwp_name);
+			ret = (*plg->descriptor->rgwp_diam_ans)(plg->cs, diam_ans, rad_ans, (void *)cli);
+			if (ret)
+				break;
+		} else {
+			TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", plg->descriptor->rgwp_name);
+		}					
+	}
+	
+	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", strerror(ret));
+		return ret;
+	}
+	
+	/* We might define other return values with special meaning here (ret == -1, ...) for example create a new Diameter request */
+	
+	/* -1: just abord the translation with no more processing. */
+	
+	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->cc);
+		if (plg->descriptor && plg->descriptor->rgwp_conf_free ) {
+			TRACE_DEBUG(INFO, "RADIUS/Diameter gateway plugin '%s' cleaning up...", plg->descriptor->rgwp_name);
+			(*plg->descriptor->rgwp_conf_free)(plg->cs);
+		}
+		if (plg->dlo)
+			dlclose(plg->dlo);
+		free(plg);
+	}
+	
+	CHECK_POSIX_DO( pthread_rwlock_unlock( &plg_lock), );
+}
diff --git a/extensions/app_radgw/rgw_servers.c b/extensions/app_radgw/rgw_servers.c
new file mode 100644
index 0000000..d524f20
--- /dev/null
+++ b/extensions/app_radgw/rgw_servers.c
@@ -0,0 +1,304 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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];
+	
+	LOG_D(" auth server:");
+	LOG_D("    disabled..... : %s", rgw_servers.auth_serv.disabled ? "TRUE":"false");
+	LOG_D("    IP disabled.. : %s", rgw_servers.auth_serv.ip_disabled ? "TRUE":"false");
+	LOG_D("    IPv6 disabled : %s", rgw_servers.auth_serv.ip6_disabled ? "TRUE":"false");
+	LOG_D("    port......... : %hu", ntohs(rgw_servers.auth_serv.port));
+	inet_ntop(AF_INET, &rgw_servers.auth_serv.ip_endpoint,ipstr,sizeof(ipstr));
+	LOG_D("    IP bind...... : %s", ipstr);
+	inet_ntop(AF_INET6, &rgw_servers.auth_serv.ip6_endpoint,ipstr,sizeof(ipstr));
+	LOG_D("    IPv6 bind.... : %s", ipstr);
+
+	LOG_D(" acct server:");
+	LOG_D("    disabled..... : %s", rgw_servers.acct_serv.disabled ? "TRUE":"false");
+	LOG_D("    IP disabled.. : %s", rgw_servers.acct_serv.ip_disabled ? "TRUE":"false");
+	LOG_D("    IPv6 disabled : %s", rgw_servers.acct_serv.ip6_disabled ? "TRUE":"false");
+	LOG_D("    port......... : %hu", ntohs(rgw_servers.acct_serv.port));
+	inet_ntop(AF_INET, &rgw_servers.acct_serv.ip_endpoint,ipstr,sizeof(ipstr));
+	LOG_D("    IP bind...... : %s", ipstr);
+	inet_ntop(AF_INET6, &rgw_servers.acct_serv.ip6_endpoint,ipstr,sizeof(ipstr));
+	LOG_D("    IPv6 bind.... : %s", 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;
+		char sa_buf[sSA_DUMP_STRLEN];
+		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 */
+		port = sSAport(&from);
+		if (!port) {
+			LOG_E("Invalid port (family: %d), discarding received %d bytes...", from.ss_family, len);
+			continue;
+		}
+		
+		fd_sa_sdump_numeric(sa_buf, (sSA*)&from);
+		LOG_D("RADIUS: RCV %dB from %s", len, sa_buf);
+		
+		/* Search the associated client definition, if any */
+		CHECK_FCT_DO( rgw_clients_search((struct sockaddr *) &from, &nas_info),
+			{
+				LOG_E("Discarding %d bytes received from unknown IP: %s", len, sa_buf);
+				continue;
+			} );
+				
+		
+		/* parse the message, loop if message is invalid */
+		CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg), 
+			{
+				DiamId_t cliname = NULL;
+				size_t clisz;
+				CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, &clisz, NULL, NULL), );
+				LOG_E( "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, 1);
+		
+		/* 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;
+	char sa_buf[sSA_DUMP_STRLEN];
+	
+	/* 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) {
+		LOG_E( "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;
+	}
+	
+	fd_sa_sdump_numeric(sa_buf, (sSA*)&sto);
+	LOG_D("RADIUS: SND %zdB   to %s", buflen, sa_buf);
+	
+	/* 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;
+	}
+	
+}
+
+
diff --git a/extensions/app_radgw/rgw_worker.c b/extensions/app_radgw/rgw_worker.c
new file mode 100644
index 0000000..25cb146
--- /dev/null
+++ b/extensions/app_radgw/rgw_worker.c
@@ -0,0 +1,346 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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 */
+};
+
+/* 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)
+{
+	TRACE_ENTRY("%p", arg);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "radgw/worker #%ld", (long)arg);
+		fd_log_threadname ( buf );
+	}
+	
+	while (1) { /* The thread will be cancelled */
+		
+		struct rgw_radius_msg_meta * msg;
+		struct rgw_client * cli;
+		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_clients_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 */
+		}
+		
+		diam_msg = NULL;
+		/* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore. */
+	
+		/* Check that IP is coherent with the identity in the message, and create an empty message with only Origin information */
+		CHECK_FCT_DO( rgw_clients_create_origin(msg, cli, &diam_msg),
+			{
+				/* An error occurred, discard message */
+				if (diam_msg) {
+					CHECK_FCT_DO( fd_msg_free(diam_msg), );
+				}
+				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, &diam_msg, cli), 
+			{
+				/* An error occurred, discard message */
+				if (diam_msg) {
+					CHECK_FCT_DO( fd_msg_free(diam_msg), );
+					diam_msg = NULL;
+				}
+				rgw_msg_free(&msg);
+				rgw_clients_dispose(&cli);
+				continue;
+			}  );
+		if (msg == NULL) { /* Error or RADIUS answer locally generated */
+			rgw_clients_dispose(&cli);
+			if (diam_msg) {
+				CHECK_FCT_DO( fd_msg_free(diam_msg), );
+				diam_msg = NULL;
+			}
+			continue; /* the message was handled already */
+		}
+		
+		pb = 0;
+		
+		/* Check the created Diameter message -- it will be invalid if no callback has handled the RADIUS message */
+		if ((diam_msg == NULL) || ( fd_msg_parse_rules(diam_msg, fd_g_config->cnf_dict, NULL) ) ) {
+			fd_log_debug("[radgw] No or invalid Diameter message was generated after processing the RADIUS command %hhd (%s)."
+					" It may indicate a gateway configuration problem, or implementation issue in a plugin.",
+					msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
+			/* We should also dump the conflicting rule here to help debug? */
+			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.",
+					attr->type, rgw_msg_attrtype_str(attr->type),
+					msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
+		}
+		
+		if (pb) {
+			/* Something went wrong during the conversion */
+			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.", 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;
+		
+		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.");
+
+				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, ans, &rad_ans, pa->cli), goto out );
+
+	if (*ans != NULL) {
+
+		/* 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_SESSION_ID:
+						case DIAM_ATTR_ROUTE_RECORD:
+						case DIAM_ATTR_PROXY_INFO:
+
+
+							/* just ignore */
+							break;
+
+						default:
+							LOG_D("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! Please use debug.rgwx for more information.", pb);
+		}
+	}
+	
+	
+out:
+	/* Now try and send the RADIUS answer */
+	if (rad_ans) {
+		CHECK_FCT_DO( rgw_client_finish_send(&rad_ans, pa->rad, pa->cli), );	
+	} else {
+		/* Remove the request from the duplicate cache */
+		CHECK_FCT_DO( rgw_client_finish_nosend(pa->rad, pa->cli), );
+	}
+
+	/* Clear the Diameter message */
+	if (*ans) {
+		CHECK_FCT_DO( fd_msg_free(*ans),  );
+		*ans = NULL;
+	}
+	
+	/* Clear the RADIUS request */
+	if (pa->rad) {
+		rgw_msg_free(&pa->rad);
+	}
+	
+	/* Release reference on the client */
+	rgw_clients_dispose(&pa->cli);
+	
+	/* Clear the answer data */
+	free(pa);
+	
+	/* Finished */
+	return;
+}
+
+int rgw_work_start(void)
+{
+	long i;
+	TRACE_ENTRY();
+	
+	memset(workers, 0, sizeof(workers));
+	
+	CHECK_FCT( fd_fifo_new ( &work_stack, 30 ) );
+	
+	/* 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;
+}
diff --git a/extensions/app_radgw/rgwx_acct.c b/extensions/app_radgw/rgwx_acct.c
new file mode 100644
index 0000000..f9f1480
--- /dev/null
+++ b/extensions/app_radgw/rgwx_acct.c
@@ -0,0 +1,1463 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* RADIUS Accounting-Request messages translation plugin */
+
+#include "rgw_common.h"
+
+
+/* Other constants we use */
+#define AI_ACCT			3	/* Diameter Base Accounting application */
+#define CC_AC			271	/* ACR/ACA */
+#define ACV_ART_START_RECORD		2	/* START_RECORD */
+#define ACV_ART_INTERIM_RECORD		3	/* INTERIM_RECORD */
+#define ACV_ART_STOP_RECORD		4	/* STOP_RECORD */
+#define ACV_ART_AUTHORIZE_AUTHENTICATE	3	/* AUTHORIZE_AUTHENTICATE */
+
+
+/* The state we keep for this plugin */
+struct rgwp_config {
+	struct {
+		struct dict_object * Accounting_Record_Number;	/* Accounting-Record-Number */
+		struct dict_object * Accounting_Record_Type;	/* Accounting-Record-Type */
+		struct dict_object * Acct_Application_Id;	/* Acct-Application-Id */
+		struct dict_object * Acct_Delay_Time;		/* Acct-Delay-Time */
+		struct dict_object * Accounting_Input_Octets;	/* Accounting-Input-Octets */
+		struct dict_object * Accounting_Output_Octets;	/* Accounting-Output-Octets */
+		struct dict_object * Accounting_Input_Packets;	/* Accounting-Input-Packets */
+		struct dict_object * Accounting_Output_Packets;	/* Accounting-Output-Packets */
+		struct dict_object * Acct_Link_Count;		/* Acct-Link-Count */
+		struct dict_object * Acct_Authentic;		/* Acct-Authentic */
+		struct dict_object * Acct_Multi_Session_Id;	/* Acct-Multi-Session-Id */
+		struct dict_object * Acct_Session_Id;		/* Acct-Session-Id */
+		struct dict_object * Acct_Session_Time;		/* Acct-Session-Time */
+		
+		struct dict_object * ARAP_Password;		/* ARAP-Password */
+		struct dict_object * ARAP_Security;		/* ARAP-Security */
+		struct dict_object * ARAP_Security_Data;	/* ARAP-Security-Data */
+		struct dict_object * Auth_Application_Id;	/* Auth-Application-Id */
+		struct dict_object * Auth_Request_Type;		/* Auth-Request-Type */
+		struct dict_object * Authorization_Lifetime;	/* Authorization-Lifetime */
+		struct dict_object * Callback_Number;		/* Callback-Number */
+		struct dict_object * Callback_Id;		/* Callback-Id */
+		struct dict_object * Called_Station_Id;		/* Called-Station-Id */
+		struct dict_object * Calling_Station_Id;	/* Calling-Station-Id */
+		struct dict_object * Class;			/* Class */
+		struct dict_object * CHAP_Algorithm;		/* CHAP-Algorithm */
+		struct dict_object * CHAP_Auth;			/* CHAP-Auth */
+		struct dict_object * CHAP_Challenge;		/* CHAP-Challenge */
+		struct dict_object * CHAP_Ident;		/* CHAP-Ident */
+		struct dict_object * CHAP_Response;		/* CHAP-Response */
+		struct dict_object * Connect_Info;		/* Connect-Info */
+		struct dict_object * Destination_Host;		/* Destination-Host */
+		struct dict_object * Destination_Realm;		/* Destination-Realm */
+		struct dict_object * EAP_Payload;		/* EAP-Payload */
+		struct dict_object * Error_Message;		/* Error-Message */
+		struct dict_object * Error_Reporting_Host;	/* Error-Reporting-Host */
+		struct dict_object * Event_Timestamp;		/* Event-Timestamp */
+		struct dict_object * Failed_AVP;		/* Failed-AVP */
+		struct dict_object * Framed_AppleTalk_Link;	/* Framed-AppleTalk-Link */
+		struct dict_object * Framed_AppleTalk_Network;	/* Framed-AppleTalk-Network */
+		struct dict_object * Framed_AppleTalk_Zone;	/* Framed-AppleTalk-Zone */
+		struct dict_object * Framed_Compression;	/* Framed-Compression */
+		struct dict_object * Framed_IP_Address;		/* Framed-IP-Address */
+		struct dict_object * Framed_IP_Netmask;		/* Framed-IP-Netmask */
+		struct dict_object * Framed_Interface_Id;	/* Framed-Interface-Id */
+		struct dict_object * Framed_IPv6_Prefix;	/* Framed-IPv6-Prefix */
+		struct dict_object * Framed_IPX_Network;	/* Framed-IPX-Network */
+		struct dict_object * Framed_MTU;		/* Framed-MTU */
+		struct dict_object * Framed_Protocol;		/* Framed-Protocol */
+		struct dict_object * Framed_Pool;		/* Framed-Pool */
+		struct dict_object * Framed_IPv6_Route;		/* Framed-IPv6-Route */
+		struct dict_object * Framed_IPv6_Pool;		/* Framed-IPv6-Pool */
+		struct dict_object * Framed_Route;		/* Framed-Route */
+		struct dict_object * Framed_Routing;		/* Framed-Routing */
+		struct dict_object * Filter_Id;			/* Filter-Id */
+		struct dict_object * Idle_Timeout;		/* Idle-Timeout */
+		struct dict_object * Login_IP_Host;		/* Login-IP-Host */
+		struct dict_object * Login_IPv6_Host;		/* Login-IPv6-Host */
+		struct dict_object * Login_LAT_Group;		/* Login-LAT-Group */
+		struct dict_object * Login_LAT_Node;		/* Login-LAT-Node */
+		struct dict_object * Login_LAT_Port;		/* Login-LAT-Port */
+		struct dict_object * Login_LAT_Service;		/* Login-LAT-Service */
+		struct dict_object * Login_Service;		/* Login-Service */
+		struct dict_object * Login_TCP_Port;		/* Login-TCP-Port */
+		struct dict_object * NAS_Identifier;		/* NAS-Identifier */
+		struct dict_object * NAS_IP_Address;		/* NAS-IP-Address */
+		struct dict_object * NAS_IPv6_Address;		/* NAS-IPv6-Address */
+		struct dict_object * NAS_Port;			/* NAS-Port */
+		struct dict_object * NAS_Port_Id;		/* NAS-Port-Id */
+		struct dict_object * NAS_Port_Type;		/* NAS-Port-Type */
+		struct dict_object * Origin_AAA_Protocol;	/* Origin-AAA-Protocol */
+		struct dict_object * Origin_Host;		/* Origin-Host */
+		struct dict_object * Origin_Realm;		/* Origin-Realm */
+		struct dict_object * Originating_Line_Info;	/* Originating-Line-Info */
+		struct dict_object * Port_Limit;		/* Port-Limit */
+		struct dict_object * Re_Auth_Request_Type;	/* Re-Auth-Request-Type */
+		struct dict_object * Result_Code;		/* Result-Code */
+		struct dict_object * Service_Type;		/* Service-Type */
+		struct dict_object * Session_Id;		/* Session-Id */
+		struct dict_object * Session_Timeout;		/* Session-Timeout */
+		struct dict_object * State;			/* State */
+		struct dict_object * Termination_Cause;		/* Termination-Cause */
+		struct dict_object * Tunneling;			/* Tunneling */
+		struct dict_object * Tunnel_Type;		/* Tunnel-Type */
+		struct dict_object * Tunnel_Assignment_Id;	/* Tunnel-Assignment-Id */
+		struct dict_object * Tunnel_Medium_Type;	/* Tunnel-Medium-Type */
+		struct dict_object * Tunnel_Client_Endpoint;	/* Tunnel-Client-Endpoint */
+		struct dict_object * Tunnel_Server_Endpoint;	/* Tunnel-Server-Endpoint */
+		struct dict_object * Tunnel_Private_Group_Id;	/* Tunnel-Private-Group-Id */
+		struct dict_object * Tunnel_Preference;		/* Tunnel-Preference */
+		struct dict_object * Tunnel_Client_Auth_Id;	/* Tunnel-Client-Auth-Id */
+		struct dict_object * Tunnel_Server_Auth_Id;	/* Tunnel-Server-Auth-Id */
+		struct dict_object * User_Name;			/* User-Name */
+		
+		struct dict_object * Session_Termination_Request;/* STR */
+	} dict; /* cache of the dictionary objects we use */
+	struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */
+	char * confstr;
+	
+	int ignore_nai;
+};
+
+/* The state we store in the session */
+struct sess_state {
+	application_id_t auth_appl;	/* Auth-Application-Id used for this session, if available (stored in a Class attribute) */
+	int		 send_str;	/* If not 0, we must send a STR when the ACA is received. */
+	uint32_t	 term_cause;	/* If not 0, the Termination-Cause to put in the STR. */
+};
+
+static DECLARE_FD_DUMP_PROTOTYPE(acct_conf_session_state_dump, struct sess_state * st)
+{
+	return fd_dump_extend( FD_DUMP_STD_PARAMS, "[rgwx sess_state](@%p): aai:%x str:%d TC:%u", st, st->auth_appl, st->send_str, st->term_cause);
+}
+
+/* Initialize the plugin */
+static int acct_conf_parse(char * conffile, struct rgwp_config ** state)
+{
+	struct rgwp_config * new;
+	struct dict_object * app;
+	
+	TRACE_ENTRY("%p %p", conffile, state);
+	CHECK_PARAMS( state );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
+	memset(new, 0, sizeof(struct rgwp_config));
+	
+	CHECK_FCT( fd_sess_handler_create( &new->sess_hdl, (void *)free, acct_conf_session_state_dump, NULL ) );
+	new->confstr = conffile;
+	
+	if (conffile && strstr(conffile, "nonai"))
+		new->ignore_nai = 1;
+	
+	/* Resolve all dictionary objects we use */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &new->dict.Accounting_Record_Number, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &new->dict.Accounting_Record_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &new->dict.Acct_Application_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Delay-Time", &new->dict.Acct_Delay_Time, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Input-Octets", &new->dict.Accounting_Input_Octets, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Output-Octets", &new->dict.Accounting_Output_Octets, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Input-Packets", &new->dict.Accounting_Input_Packets, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Output-Packets", &new->dict.Accounting_Output_Packets, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Authentic", &new->dict.Acct_Authentic, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Link-Count", &new->dict.Acct_Link_Count, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Multi-Session-Id", &new->dict.Acct_Multi_Session_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Session-Id", &new->dict.Acct_Session_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Session-Time", &new->dict.Acct_Session_Time, ENOENT) );
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Password", &new->dict.ARAP_Password, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security", &new->dict.ARAP_Security, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security-Data", &new->dict.ARAP_Security_Data, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &new->dict.Auth_Application_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Request-Type", &new->dict.Auth_Request_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Authorization-Lifetime", &new->dict.Authorization_Lifetime, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Number", &new->dict.Callback_Number, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Id", &new->dict.Callback_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Called-Station-Id", &new->dict.Called_Station_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Calling-Station-Id", &new->dict.Calling_Station_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Class", &new->dict.Class, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Connect-Info", &new->dict.Connect_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &new->dict.Destination_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &new->dict.Destination_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "EAP-Payload", &new->dict.EAP_Payload, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &new->dict.Error_Message, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &new->dict.Error_Reporting_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Event-Timestamp", &new->dict.Event_Timestamp, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &new->dict.Failed_AVP, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Link", &new->dict.Framed_AppleTalk_Link, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Network", &new->dict.Framed_AppleTalk_Network, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Zone", &new->dict.Framed_AppleTalk_Zone, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Compression", &new->dict.Framed_Compression, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Address", &new->dict.Framed_IP_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Netmask", &new->dict.Framed_IP_Netmask, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Interface-Id", &new->dict.Framed_Interface_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Prefix", &new->dict.Framed_IPv6_Prefix, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPX-Network", &new->dict.Framed_IPX_Network, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-MTU", &new->dict.Framed_MTU, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Protocol", &new->dict.Framed_Protocol, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Pool", &new->dict.Framed_Pool, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Route", &new->dict.Framed_Route, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Route", &new->dict.Framed_IPv6_Route, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Pool", &new->dict.Framed_IPv6_Pool, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Routing", &new->dict.Framed_Routing, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Filter-Id", &new->dict.Filter_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Idle-Timeout", &new->dict.Idle_Timeout, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IP-Host", &new->dict.Login_IP_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IPv6-Host", &new->dict.Login_IPv6_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Group", &new->dict.Login_LAT_Group, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Node", &new->dict.Login_LAT_Node, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Port", &new->dict.Login_LAT_Port, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Service", &new->dict.Login_LAT_Service, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-Service", &new->dict.Login_Service, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-TCP-Port", &new->dict.Login_TCP_Port, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Identifier", &new->dict.NAS_Identifier, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IP-Address", &new->dict.NAS_IP_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IPv6-Address", &new->dict.NAS_IPv6_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port", &new->dict.NAS_Port, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Id", &new->dict.NAS_Port_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Type", &new->dict.NAS_Port_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-AAA-Protocol", &new->dict.Origin_AAA_Protocol, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &new->dict.Origin_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &new->dict.Origin_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Originating-Line-Info", &new->dict.Originating_Line_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Port-Limit", &new->dict.Port_Limit, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Re-Auth-Request-Type", &new->dict.Re_Auth_Request_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &new->dict.Result_Code, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Service-Type", &new->dict.Service_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Timeout", &new->dict.Session_Timeout, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "State", &new->dict.State, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Termination-Cause", &new->dict.Termination_Cause, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunneling", &new->dict.Tunneling, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Assignment-Id", &new->dict.Tunnel_Assignment_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Type", &new->dict.Tunnel_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Medium-Type", &new->dict.Tunnel_Medium_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Endpoint", &new->dict.Tunnel_Client_Endpoint, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Endpoint", &new->dict.Tunnel_Server_Endpoint, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Private-Group-Id", &new->dict.Tunnel_Private_Group_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Preference", &new->dict.Tunnel_Preference, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Auth-Id", &new->dict.Tunnel_Client_Auth_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Auth-Id", &new->dict.Tunnel_Server_Auth_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) );
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Session-Termination-Request", &new->dict.Session_Termination_Request, ENOENT) );
+	
+	/* This plugin provides the following Diameter authentication applications support: */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Base Accounting", &app, ENOENT) );
+	CHECK_FCT( fd_disp_app_support ( app, NULL, 0, 1 ) );
+	
+	*state = new;
+	return 0;
+}
+
+/* deinitialize */
+static void acct_conf_free(struct rgwp_config * state)
+{
+	TRACE_ENTRY("%p", state);
+	CHECK_PARAMS_DO( state, return );
+	CHECK_FCT_DO( fd_sess_handler_destroy( &state->sess_hdl, NULL ),  );
+	free(state);
+	return;
+}
+
+/* Incoming RADIUS request */
+static int acct_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+{
+	int idx;
+	int send_str=0;
+	uint32_t str_cause=0;
+	uint32_t e2eid = 0;
+	application_id_t auth_appl=0;
+	int got_id = 0;
+	uint32_t status_type;
+	uint32_t termination_action = 0;
+	uint32_t gigawords_in=0, gigawords_out=0;
+	size_t nattr_used = 0;
+	union avp_value value;
+	struct avp ** avp_tun = NULL, *avp = NULL;
+	struct session * sess;
+	
+	const char * prefix = "Diameter/";
+	size_t pref_len;
+	os0_t si = NULL;
+	size_t si_len = 0;
+	os0_t un = NULL;
+	size_t un_len = 0;
+	
+	TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
+	CHECK_PARAMS(rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCOUNTING_REQUEST) && rad_ans && diam_fw && *diam_fw);
+	
+	pref_len = strlen(prefix);
+	
+	/*
+	      Either NAS-IP-Address or NAS-Identifier MUST be present in a
+	      RADIUS Accounting-Request.  It SHOULD contain a NAS-Port or NAS-
+	      Port-Type attribute or both unless the service does not involve a
+	      port or the NAS does not distinguish among its ports.
+	*/
+	/* We also enforce that the message contains a CLASS attribute with Diameter/ prefix containing the Session-Id. */
+	for (idx = 0; idx < rad_req->attr_used; idx++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+		uint8_t * v = (uint8_t *)(attr + 1);
+		size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
+		
+		switch (attr->type) {
+			case RADIUS_ATTR_NAS_IP_ADDRESS:
+			case RADIUS_ATTR_NAS_IDENTIFIER:
+			case RADIUS_ATTR_NAS_IPV6_ADDRESS:
+				got_id = 1;
+				break;
+				
+			case RADIUS_ATTR_TERMINATION_ACTION:
+				CHECK_PARAMS( attr->length == 6 );
+				termination_action  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+				break;
+				
+			case RADIUS_ATTR_ACCT_INPUT_GIGAWORDS:
+				CHECK_PARAMS( attr->length == 6 );
+				gigawords_in  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+				break;
+				
+			case RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS:
+				CHECK_PARAMS( attr->length == 6 );
+				gigawords_out  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+				break;
+				
+			case RADIUS_ATTR_CLASS:
+				if ((attr_len > pref_len ) && ! strncmp((char *)v, prefix, pref_len)) {
+					int i;
+					si = v + pref_len;
+					si_len = attr_len - pref_len;
+					TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), SI:'%.*s'.", prefix, idx, (int)si_len, si);
+					/* Remove from the message */
+					for (i = idx + 1; i < rad_req->attr_used; i++)
+						rad_req->attr_pos[i - 1] = rad_req->attr_pos[i];
+					rad_req->attr_used -= 1;
+				}
+				break;
+
+			case RADIUS_ATTR_USER_NAME:
+				if (attr_len) {
+					un = v;
+					un_len = attr_len;
+					TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", (int)un_len, un);
+				}
+				break;
+			
+		}
+	}
+	
+	/* Check basic information is there */
+	if (!got_id || radius_msg_get_attr_int32(rad_req, RADIUS_ATTR_ACCT_STATUS_TYPE, &status_type)) {
+		TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request from %s did not contain a NAS ip/identifier or Acct-Status-Type attribute, reject.", rgw_clients_id(cli));
+		return EINVAL;
+	}
+	
+	/*
+	      -- RFC2866:
+	      In Accounting-Request Packets, the Authenticator value is a 16
+	      octet MD5 [5] checksum, called the Request Authenticator.
+
+	      The NAS and RADIUS accounting server share a secret.  The Request
+	      Authenticator field in Accounting-Request packets contains a one-
+	      way MD5 hash calculated over a stream of octets consisting of the
+	      Code + Identifier + Length + 16 zero octets + request attributes +
+	      shared secret (where + indicates concatenation).  The 16 octet MD5
+	      hash value is stored in the Authenticator field of the
+	      Accounting-Request packet.
+
+	      Note that the Request Authenticator of an Accounting-Request can
+	      not be done the same way as the Request Authenticator of a RADIUS
+	      Access-Request, because there is no User-Password attribute in an
+	      Accounting-Request.
+	      
+	      
+	      -- RFC5080:
+	      The Request Authenticator field MUST contain the correct data, as
+	      given by the above calculation.  Invalid packets are silently
+	      discarded.  Note that some early implementations always set the
+	      Request Authenticator to all zeros.  New implementations of RADIUS
+	      clients MUST use the above algorithm to calculate the Request
+	      Authenticator field.  New RADIUS server implementations MUST
+	      silently discard invalid packets.
+	      
+	*/	
+	{
+		uint8_t save[MD5_MAC_LEN];
+		uint8_t * secret;
+		size_t secret_len;
+		
+		/* Get the shared secret */
+		CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
+		
+		/* Copy the received Request Authenticator */
+		memcpy(&save[0], &rad_req->hdr->authenticator[0], MD5_MAC_LEN);
+		
+		/* Compute the same authenticator */
+		radius_msg_finish_acct(rad_req, secret, secret_len);
+		
+		/* And now compare with the received value */
+		if (memcmp(&save[0], &rad_req->hdr->authenticator[0], MD5_MAC_LEN)) {
+			/* Invalid authenticator */
+			TRACE_BUFFER(FD_LOG_DEBUG, FULL+1, "Received ReqAuth: ", &save[0], MD5_MAC_LEN, "" );
+			TRACE_BUFFER(FD_LOG_DEBUG, FULL+1, "Expected ReqAuth: ", &rad_req->hdr->authenticator[0], MD5_MAC_LEN, "" );
+			TRACE_DEBUG(INFO, "[acct.rgwx] Invalid Request Authenticator in Account-Request from %s, discarding the message.", rgw_clients_id(cli));
+			return EINVAL;
+		}
+	}
+	
+	
+	/* Handle the Accounting-On case: nothing to do, just reply OK */
+	if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON) {
+		TRACE_DEBUG(FULL, "[acct.rgwx] Received Accounting-On Acct-Status-Type attribute, replying without translation to Diameter.");
+		CHECK_MALLOC( *rad_ans = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, rad_req->hdr->identifier) );
+		return -2;
+	}
+	
+	if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF) {
+		TRACE_DEBUG(FULL, "[acct.rgwx] Received Accounting-Off Acct-Status-Type attribute, we must terminate all active sessions.");
+		TODO("RADIUS side is rebooting, send STR on all sessions???");
+		return ENOTSUP;
+	}
+	
+	/* Check if we got a valid session information, otherwise the server will not be able to handle the data... */
+	if (!si) {
+		TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request from %s did not contain a CLASS attribute with Diameter session information, reject.", rgw_clients_id(cli));
+		return EINVAL;
+	}
+	
+	/* Add the Destination-Realm */
+	CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
+	idx = 0;
+	if (un && ! cs->ignore_nai) {
+		/* Is there an '@' in the user name? We don't care for decorated NAI here */
+		for (idx = un_len - 2; idx > 0; idx--) {
+			if (un[idx] == '@') {
+				idx++;
+				break;
+			}
+		}
+	}
+	if (idx == 0) {
+		/* Not found in the User-Name => we use the local domain of this gateway */
+		value.os.data = (uint8_t *)fd_g_config->cnf_diamrlm;
+		value.os.len  = fd_g_config->cnf_diamrlm_len;
+	} else {
+		value.os.data = un + idx;
+		value.os.len  = un_len - idx;
+	}
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+	
+	/* Create the Session-Id AVP */
+	{
+		CHECK_FCT( fd_sess_fromsid_msg ( si, si_len, &sess, NULL) );
+		
+		TRACE_DEBUG(FULL, "[acct.rgwx] Translating new accounting message for session '%.*s'...", (int)si_len, si);
+		
+		/* Add the Session-Id AVP as first AVP */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
+		value.os.data = (unsigned char *)si;
+		value.os.len = si_len;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+		CHECK_FCT( fd_msg_sess_set(*diam_fw, sess) );
+	}
+	
+		
+	/* Add the command code */
+	{
+		struct msg_hdr * header = NULL;
+		CHECK_FCT( fd_msg_hdr ( *diam_fw, &header ) );
+		header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
+		header->msg_code = CC_AC;
+		header->msg_appl = AI_ACCT;
+		
+		/* Add the Acct-Application-Id */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Acct_Application_Id, 0, &avp ) );
+		value.i32 = header->msg_appl;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+		
+		/* save the end to end id */
+		e2eid = header->msg_eteid;
+	}
+	
+	/* Convert the RADIUS attributes, as they appear in the message */
+	for (idx = 0; idx < rad_req->attr_used; idx++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+
+		switch (attr->type) {
+			/*	
+			      Any attribute valid in a RADIUS Access-Request or Access-Accept
+			      packet is valid in a RADIUS Accounting-Request packet, except that
+			      the following attributes MUST NOT be present in an Accounting-
+			      Request:  User-Password, CHAP-Password, Reply-Message, State.
+			*/
+			case RADIUS_ATTR_USER_PASSWORD:
+			case RADIUS_ATTR_CHAP_PASSWORD:
+			case RADIUS_ATTR_REPLY_MESSAGE:
+			case RADIUS_ATTR_STATE:
+			case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
+			case RADIUS_ATTR_EAP_MESSAGE:
+				TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request contains a forbidden attribute (%hhd), reject.", attr->type);
+				return EINVAL;
+				
+			
+			/* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
+			#define CONV2DIAM_STR( _dictobj_ )	\
+				CHECK_PARAMS( attr->length >= 2 );						\
+				/* Create the AVP with the specified dictionary model */			\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				value.os.len = attr->length - 2;						\
+				value.os.data = (unsigned char *)(attr + 1);					\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				/* Add the AVP in the Diameter message. */					\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+			/* Same thing, for scalar AVPs of 32 bits */
+			#define CONV2DIAM_32B( _dictobj_ )	\
+				CHECK_PARAMS( attr->length == 6 );						\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u32  = (v[0] << 24)						\
+					           | (v[1] << 16)						\
+					           | (v[2] <<  8)						\
+					           |  v[3] ;							\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+			/* And the 64b version */
+			#define CONV2DIAM_64B( _dictobj_ )	\
+				CHECK_PARAMS( attr->length == 10);						\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u64  = ((uint64_t)(v[0]) << 56)					\
+					           | ((uint64_t)(v[1]) << 48)					\
+					           | ((uint64_t)(v[2]) << 40)					\
+					           | ((uint64_t)(v[3]) << 32)					\
+					           | ((uint64_t)(v[4]) << 24)					\
+					           | ((uint64_t)(v[5]) << 16)					\
+					           | ((uint64_t)(v[6]) <<  8)					\
+					           |  (uint64_t)(v[7]) ;					\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+			
+			/* Attributes as listed in RFC2866, section 5.13  and RFC4005, section 10.2.1 */
+			case RADIUS_ATTR_USER_NAME:
+				CONV2DIAM_STR( User_Name );
+				break;
+				
+			case RADIUS_ATTR_NAS_IP_ADDRESS:
+				CONV2DIAM_STR( NAS_IP_Address );
+				break;
+				
+			case RADIUS_ATTR_NAS_PORT:
+				CONV2DIAM_32B( NAS_Port );
+				break;
+				
+			case RADIUS_ATTR_SERVICE_TYPE:
+				CONV2DIAM_32B( Service_Type );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_PROTOCOL:
+				CONV2DIAM_32B( Framed_Protocol );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IP_ADDRESS:
+				CONV2DIAM_STR( Framed_IP_Address );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IP_NETMASK:
+				CONV2DIAM_STR( Framed_IP_Netmask );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_ROUTING:
+				CONV2DIAM_32B( Framed_Routing );
+				break;
+				
+			case RADIUS_ATTR_FILTER_ID:
+				CONV2DIAM_STR( Filter_Id );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_MTU:
+				CONV2DIAM_32B( Framed_MTU );
+				break;
+			
+			case RADIUS_ATTR_FRAMED_COMPRESSION:
+				CONV2DIAM_32B( Framed_Compression );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_IP_HOST:
+				CONV2DIAM_STR( Login_IP_Host );
+				break;
+				
+			case RADIUS_ATTR_LOGIN_SERVICE:
+				CONV2DIAM_32B( Login_Service );
+				break;
+				
+			case RADIUS_ATTR_LOGIN_TCP_PORT:
+				CONV2DIAM_32B( Login_TCP_Port );
+				break;
+				
+			case RADIUS_ATTR_CALLBACK_NUMBER:
+				CONV2DIAM_STR( Callback_Number );
+				break;
+				
+			case RADIUS_ATTR_CALLBACK_ID:
+				CONV2DIAM_STR( Callback_Id );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_ROUTE:
+				CONV2DIAM_STR( Framed_Route );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IPX_NETWORK:
+				CONV2DIAM_32B( Framed_IPX_Network );
+				break;
+				
+			case RADIUS_ATTR_CLASS:
+				CONV2DIAM_STR( Class );
+				/* In addition, save the data in the session if it is "our" CLASS_AAI_PREFIX Class attribute */
+				{
+					char buf[32];
+					char * attr_val, *auth_val;
+					attr_val = (char *)(attr + 1);
+					auth_val = attr_val + CONSTSTRLEN(CLASS_AAI_PREFIX);
+					if (	(attr->length > sizeof(struct radius_attr_hdr) + CONSTSTRLEN(CLASS_AAI_PREFIX)  )
+						&& (attr->length < sizeof(struct radius_attr_hdr) + CONSTSTRLEN(CLASS_AAI_PREFIX) + sizeof(buf))
+						&& ! strncmp(attr_val, CLASS_AAI_PREFIX, CONSTSTRLEN(CLASS_AAI_PREFIX))) {
+					
+						memset(buf, 0, sizeof(buf));
+						memcpy(buf, auth_val, attr->length - sizeof(struct radius_attr_hdr) - CONSTSTRLEN(CLASS_AAI_PREFIX));
+						if (sscanf(buf, "%u", &auth_appl) == 1) {
+							TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), AAI:%u.", CLASS_AAI_PREFIX, idx, auth_appl);
+						}
+					}
+				}
+				break;
+			
+			case RADIUS_ATTR_VENDOR_SPECIFIC:
+				if (attr->length >= 6) {
+					uint32_t vendor_id;
+					uint8_t * c = (uint8_t *)(attr + 1);
+					
+					vendor_id = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];
+					c += 4;
+					
+					switch (vendor_id) {
+						
+						/* For the vendors we KNOW they follow the VSA recommended format, we convert following the rules of RFC4005 (9.6.2) */
+						case RADIUS_VENDOR_ID_MICROSOFT : /* RFC 2548 */
+						/* other vendors ? */
+						{
+							size_t left;
+							struct radius_attr_vendor *vtlv;
+							
+							left = attr->length - 6;
+							vtlv = (struct radius_attr_vendor *)c;
+						
+							while ((left >= 2) && (vtlv->vendor_length <= left)) {
+								/* Search our dictionary for corresponding Vendor's AVP */
+								struct dict_avp_request req;
+								struct dict_object * avp_model = NULL;
+								memset(&req, 0, sizeof(struct dict_avp_request));
+								req.avp_vendor = vendor_id;
+								req.avp_code = vtlv->vendor_type;
+								
+								CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &req, &avp_model, 0) );
+								if (!avp_model) {
+									TRACE_DEBUG(FULL, "Unknown attribute (vendor 0x%x, code 0x%x) ignored.", req.avp_vendor, req.avp_code);
+								} else {
+									CHECK_FCT( fd_msg_avp_new ( avp_model, 0, &avp ) );
+									value.os.len = vtlv->vendor_length - 2;
+									value.os.data = (unsigned char *)(vtlv + 1);
+									CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+									CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+								}
+								c += vtlv->vendor_length;
+								left -= vtlv->vendor_length;
+								vtlv = (struct radius_attr_vendor *)c;
+							}
+						}
+						break;
+						
+						/* Other vendors we KNOw how to convert the attributes would be added here... */
+						/* case RADIUS_VENDOR_ID_CISCO :
+							break; */
+						/* case RADIUS_VENDOR_ID_IETF : (extended RADIUS attributes)
+							break; */
+						
+						/* When we don't know, just discard the attribute... VSA are optional with regards to RADIUS anyway */
+						default:
+							/* do nothing */
+							TRACE_DEBUG(FULL, "VSA attribute from vendor %d discarded", vendor_id);
+							
+					}
+				}
+				break;
+				
+			case RADIUS_ATTR_SESSION_TIMEOUT:
+				/* Translation depends on Termination-Action : rfc4005#section-9.2.1 */
+				if (termination_action != RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+					CONV2DIAM_32B( Session_Timeout );
+				} else {
+					CONV2DIAM_32B( Authorization_Lifetime );
+					/* And add this additional AVP */
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.Re_Auth_Request_Type, 0, &avp ) );
+					value.u32 = ACV_ART_AUTHORIZE_AUTHENTICATE;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				}
+				break;
+				
+			case RADIUS_ATTR_IDLE_TIMEOUT:
+				CONV2DIAM_32B( Idle_Timeout );
+				break;
+			
+			case RADIUS_ATTR_TERMINATION_ACTION:
+				/* Just remove */
+				break;
+			
+			case RADIUS_ATTR_CALLED_STATION_ID:
+				CONV2DIAM_STR( Called_Station_Id );
+				break;
+			
+			case RADIUS_ATTR_CALLING_STATION_ID:
+				CONV2DIAM_STR( Calling_Station_Id );
+				break;
+			
+			case RADIUS_ATTR_NAS_IDENTIFIER:
+				CONV2DIAM_STR( NAS_Identifier );
+				break;
+			
+			/* Proxy-State is handled by echo_drop.rgwx plugin, we ignore it here */
+			
+			case RADIUS_ATTR_LOGIN_LAT_SERVICE:
+				CONV2DIAM_STR( Login_LAT_Service );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_NODE:
+				CONV2DIAM_STR( Login_LAT_Node );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_GROUP:
+				CONV2DIAM_STR( Login_LAT_Group );
+				break;
+			
+			case RADIUS_ATTR_FRAMED_APPLETALK_LINK:
+				CONV2DIAM_32B( Framed_AppleTalk_Link );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_APPLETALK_NETWORK:
+				CONV2DIAM_32B( Framed_AppleTalk_Network );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_APPLETALK_ZONE:
+				CONV2DIAM_STR( Framed_AppleTalk_Zone );
+				break;
+			
+			case RADIUS_ATTR_ACCT_STATUS_TYPE:
+				/*
+				      -  If the RADIUS message received is an Accounting-Request, the
+        				 Acct-Status-Type attribute value must be converted to a
+        				 Accounting-Record-Type AVP value.  If the Acct-Status-Type
+        				 attribute value is STOP, the local server MUST issue a
+        				 Session-Termination-Request message once the Diameter
+        				 Accounting-Answer message has been received.
+				*/
+				switch (status_type) {
+					case RADIUS_ACCT_STATUS_TYPE_START:
+						value.u32 = ACV_ART_START_RECORD;
+						break;
+					case RADIUS_ACCT_STATUS_TYPE_STOP:
+						value.u32 = ACV_ART_STOP_RECORD;
+						send_str = 1; /* Register this info in the session */
+						break;
+					case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE:
+						value.u32 = ACV_ART_INTERIM_RECORD;
+						break;
+					default:
+						TRACE_DEBUG(INFO, "Unknown RADIUS_ATTR_ACCT_STATUS_TYPE value %d, aborting...", status_type);
+						return ENOTSUP;
+				}
+				CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Record_Type, 0, &avp ) );
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				
+				/* While here, we also add the Accouting-Record-Number AVP.
+					  The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32
+					   and identifies this record within one session.  As Session-Id AVPs
+					   are globally unique, the combination of Session-Id and Accounting-
+					   Record-Number AVPs is also globally unique, and can be used in
+					   matching accounting records with confirmations.  An easy way to
+					   produce unique numbers is to set the value to 0 for records of type
+					   EVENT_RECORD and START_RECORD, and set the value to 1 for the first
+					   INTERIM_RECORD, 2 for the second, and so on until the value for
+					   STOP_RECORD is one more than for the last INTERIM_RECORD.
+					   
+				  -- we actually use the end-to-end id of the message here, which remains constant
+				    if we send a duplicate, so it has the same properties as the suggested algorithm.
+				    Anyway, it assumes that we are not converting twice the same RADIUS message.
+				   . */
+				CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Record_Number, 0, &avp ) );
+				value.u32 = e2eid;
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				
+				break;
+			
+			case RADIUS_ATTR_ACCT_DELAY_TIME:
+				CONV2DIAM_32B( Acct_Delay_Time );
+				break;
+				
+			/*
+			      -  If the RADIUS message contains the Accounting-Input-Octets,
+        			 Accounting-Input-Packets, Accounting-Output-Octets, or
+        			 Accounting-Output-Packets, these attributes must be converted
+        			 to the Diameter equivalents.  Further, if the Acct-Input-
+        			 Gigawords or Acct-Output-Gigawords attributes are present,
+        			 these must be used to properly compute the Diameter accounting
+        			 AVPs.
+			*/
+			case RADIUS_ATTR_ACCT_INPUT_OCTETS:
+				memset(&value, 0, sizeof(value));
+				{
+					uint8_t * v = (uint8_t *)(attr + 1);
+					value.u64  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+				}
+				value.u64 += ((uint64_t)gigawords_in << 32);
+				CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Input_Octets, 0, &avp ) );
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				break;
+				
+			case RADIUS_ATTR_ACCT_OUTPUT_OCTETS:
+				memset(&value, 0, sizeof(value));
+				{
+					uint8_t * v = (uint8_t *)(attr + 1);
+					value.u64  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+				}
+				value.u64 += ((uint64_t)gigawords_out << 32);
+				CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Output_Octets, 0, &avp ) );
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				break;
+				
+			case RADIUS_ATTR_ACCT_SESSION_ID:
+				CONV2DIAM_STR( Acct_Session_Id );
+				break;
+				
+			case RADIUS_ATTR_ACCT_AUTHENTIC:
+				CONV2DIAM_32B( Acct_Authentic );
+				break;
+				
+			case RADIUS_ATTR_ACCT_SESSION_TIME:
+				CONV2DIAM_32B( Acct_Session_Time );
+				break;
+				
+			case RADIUS_ATTR_ACCT_INPUT_PACKETS:
+				memset(&value, 0, sizeof(value));
+				{
+					uint8_t * v = (uint8_t *)(attr + 1);
+					value.u64  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+				}
+				/* value.u64 += (gigawords_in << 32); */
+				CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Input_Packets, 0, &avp ) );
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				break;
+				
+			case RADIUS_ATTR_ACCT_OUTPUT_PACKETS:
+				memset(&value, 0, sizeof(value));
+				{
+					uint8_t * v = (uint8_t *)(attr + 1);
+					value.u64  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+				}
+				/* value.u64 += (gigawords_out << 32); */
+				CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Output_Packets, 0, &avp ) );
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				break;
+				
+			/*
+			      -  If the Accounting message contains an Acct-Termination-Cause
+        			 attribute, it should be translated to the equivalent
+        			 Termination-Cause AVP value.
+			*/
+			case RADIUS_ATTR_ACCT_TERMINATE_CAUSE:
+				/* rfc4005#section-9.3.5 */
+				{
+					uint8_t * v = (uint8_t *)(attr + 1);
+					str_cause  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+				}
+				str_cause += 10; /* This seems to be the rule, we can modify later if needed */
+				break;
+
+			case RADIUS_ATTR_ACCT_MULTI_SESSION_ID:
+				CONV2DIAM_STR( Acct_Multi_Session_Id );
+				break;
+				
+			case RADIUS_ATTR_ACCT_LINK_COUNT:
+				CONV2DIAM_32B( Acct_Link_Count );
+				break;
+				
+			/* CHAP-Challenge is not present in Accounting-Request */
+			
+			case RADIUS_ATTR_NAS_PORT_TYPE:
+				CONV2DIAM_32B( NAS_Port_Type );
+				break;
+			
+			case RADIUS_ATTR_PORT_LIMIT:
+				CONV2DIAM_32B( Port_Limit );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_PORT:
+				CONV2DIAM_STR( Login_LAT_Port );
+				break;
+			
+			/* RFC 3162 */	
+			case RADIUS_ATTR_NAS_IPV6_ADDRESS:
+				CONV2DIAM_STR( NAS_IPv6_Address );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_INTERFACE_ID:
+				CONV2DIAM_64B( Framed_Interface_Id );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IPV6_PREFIX:
+				CONV2DIAM_STR( Framed_IPv6_Prefix );
+				break;
+				
+			case RADIUS_ATTR_LOGIN_IPV6_HOST:
+				CONV2DIAM_STR( Login_IPv6_Host );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IPV6_ROUTE:
+				CONV2DIAM_STR( Framed_IPv6_Route );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IPV6_POOL:
+				CONV2DIAM_STR( Framed_IPv6_Pool );
+				break;
+				
+			/* RFC 2868 */
+			/* Prepare the top-level Tunneling AVP for each tag values, as needed, and add to the Diameter message.
+				This macro is called when an AVP is added inside the group, so we will not have empty grouped AVPs */
+			#define AVP_TUN_PREPARE() {										\
+						if (avp_tun == NULL) {								\
+							CHECK_MALLOC( avp_tun = calloc(sizeof(struct avp *), 32 ) );		\
+						}										\
+						tag = *(uint8_t *)(attr + 1);							\
+						if (tag > 0x1F) tag = 0;							\
+						if (avp_tun[tag] == NULL) {							\
+							CHECK_FCT( fd_msg_avp_new ( cs->dict.Tunneling, 0, &avp_tun[tag] ) );	\
+							CHECK_FCT( fd_msg_avp_add (*diam_fw, MSG_BRW_LAST_CHILD, avp_tun[tag]));\
+						}										\
+					}
+			
+			/* Convert an attribute to an OctetString AVP and add inside the Tunneling AVP corresponding to the tag */
+			#define CONV2DIAM_TUN_STR( _dictobj_ ) {						\
+				uint8_t tag;									\
+				CHECK_PARAMS( attr->length >= 3);						\
+				AVP_TUN_PREPARE();								\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				value.os.len = attr->length - (tag ? 3 : 2);					\
+				value.os.data = ((unsigned char *)(attr + 1)) + (tag ? 1 : 0);			\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) );		\
+				}
+				
+			/* Convert an attribute to a scalar AVP and add inside the Tunneling AVP corresponding to the tag */
+			#define CONV2DIAM_TUN_24B( _dictobj_ ) {						\
+				uint8_t tag;									\
+				CHECK_PARAMS( attr->length == 6);						\
+				AVP_TUN_PREPARE();								\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u32 = (v[1] << 16) | (v[2] <<8) | v[3] ;				\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) );		\
+				}
+
+			/*
+			      -  If the RADIUS message contains Tunnel information [RADTunnels],
+        			 the attributes or tagged groups should each be converted to a
+        			 Diameter Tunneling Grouped AVP set.  If the tunnel information
+        			 contains a Tunnel-Password attribute, the RADIUS encryption
+        			 must be resolved, and the password forwarded, by using Diameter
+        			 security methods.
+				    -> If the RADIUS message does not use properly the Tag info, result is unpredictable here.. 
+			*/
+			case RADIUS_ATTR_TUNNEL_TYPE:
+				CONV2DIAM_TUN_24B( Tunnel_Type );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
+				CONV2DIAM_TUN_24B( Tunnel_Medium_Type );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT:
+				CONV2DIAM_TUN_STR( Tunnel_Client_Endpoint );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT:
+				CONV2DIAM_TUN_STR( Tunnel_Server_Endpoint );
+				break;
+			
+			/* Tunnel-Password never present in an Accounting-Request */
+
+			case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Private_Group_Id );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_ASSIGNMENT_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Assignment_Id );
+				break;
+			
+			/* Tunnel-Reference never present in an Accounting-Request */
+			
+			case RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Client_Auth_Id );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Server_Auth_Id );
+				break;
+			
+		/* RFC 2869 */
+			/*
+			                                      Acct-Input-Gigawords, Acct-Output-
+			   Gigawords, Event-Timestamp, and NAS-Port-Id may have 0-1 instances in
+			   an Accounting-Request packet.  Connect-Info may have 0+ instances in
+			   an Accounting-Request packet.  The other attributes added in this
+			   document must not be present in an Accounting-Request.
+			*/
+			case RADIUS_ATTR_ACCT_INPUT_GIGAWORDS:
+				break; /* we already saved the value in gigawords_in */
+				
+			case RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS:
+				break; /* we already saved the value in gigawords_out */
+				
+			case RADIUS_ATTR_EVENT_TIMESTAMP:
+				/* RADIUS: 
+				      The Value field is four octets encoding an unsigned integer with
+				      the number of seconds since January 1, 1970 00:00 UTC.
+				   Diameter:
+				      The Time format is derived from the OctetString AVP Base Format.
+				      The string MUST contain four octets, in the same format as the
+				      first four bytes are in the NTP timestamp format.  The NTP
+				      Timestamp format is defined in Chapter 3 of [RFC4330].
+
+				      This represents the number of seconds since 0h on 1 January 1900
+				      with respect to the Coordinated Universal Time (UTC).
+				      
+				      -- RFC4330:
+					 NTP timestamps are represented as a 64-bit unsigned
+					   fixed-point number, in seconds relative to 0h on 1 January 1900.  The
+					   integer part is in the first 32 bits, and the fraction part in the
+					   last 32 bits.  In the fraction part, the non-significant low-order
+					   bits are not specified and are ordinarily set to 0.
+				*/
+				{
+					uint32_t ts;
+					
+					uint8_t * v = (uint8_t *)(attr + 1);
+					/* Read the RADIUS attribute value */
+					ts  = (v[0] << 24)
+					           | (v[1] << 16)
+					           | (v[2] <<  8)
+					           |  v[3] ;
+					
+					/* Add the 70 missing years */
+					ts += 2208988800U; /* 60 * 60 * 24 * ( 365 * 70 + 17 ) */
+					
+					/* Convert to network byte order */
+					ts = htonl(ts);
+					
+					 /* Diameter Time datatype is derived from OctetString */
+					value.os.data = (void *) &ts;
+					value.os.len = sizeof(uint32_t);
+					
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.Event_Timestamp, 0, &avp ) );
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				}
+				break;
+			
+			case RADIUS_ATTR_NAS_PORT_ID:
+				CONV2DIAM_STR( NAS_Port_Id );
+				break;
+						
+			case RADIUS_ATTR_CONNECT_INFO:
+				CONV2DIAM_STR( Connect_Info );
+				break;
+			
+			case RADIUS_ATTR_FRAMED_POOL: /* To follow the IPv6 version */
+				CONV2DIAM_STR( Framed_Pool );
+				break;
+				
+			
+		/* RFC 3579 */
+			/*
+			 The EAP-Message and Message-Authenticator attributes specified in
+			   this document MUST NOT be present in an Accounting-Request.
+			*/				
+			case RADIUS_ATTR_ORIGINATING_LINE_INFO:
+				CONV2DIAM_STR( Originating_Line_Info );
+				break;
+				
+		/* Default */		
+			default: /* unknown attribute */
+				/* We just keep the attribute in the RADIUS message */
+				rad_req->attr_pos[nattr_used++] = rad_req->attr_pos[idx];
+		}
+	}
+	
+	/* Update the radius message to remove all handled attributes */
+	rad_req->attr_used = nattr_used;
+	
+	/* Store useful information in the session */
+	{
+		struct sess_state * st;
+		
+		CHECK_MALLOC( st = malloc(sizeof(struct sess_state)) );
+		memset(st, 0, sizeof(struct sess_state));
+		st->auth_appl = auth_appl;
+		if (auth_appl) { /* We use the value 0 for servers which indicated NO STATE MAINTAINED, hence have no need for STR */
+			st->send_str = send_str;
+		}
+		st->term_cause = str_cause;
+		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, sess, &st ) );
+	}
+	
+	return 0;
+}
+
+/* Callback when an STA is received after having sent an STR. */
+static void handle_sta(void * data, struct msg ** answer)
+{
+	struct rgwp_config * cs = data;
+	struct avp *avp;
+	struct avp_hdr *ahdr;
+	
+	CHECK_PARAMS_DO( data && answer && *answer, goto out );
+	
+	/* Check the Diameter error code */
+	CHECK_FCT_DO( fd_msg_search_avp (*answer, cs->dict.Result_Code, &avp), goto out );
+	CHECK_PARAMS_DO( avp, goto out );
+	CHECK_FCT_DO( fd_msg_avp_hdr ( avp, &ahdr ), goto out );
+	if (ahdr->avp_value->u32 != ER_DIAMETER_SUCCESS)
+		goto out;
+	
+	/* OK, discard the message without complaining */
+	fd_msg_free(*answer);
+	*answer = NULL;
+		
+out:
+	if (answer && *answer) {
+		char * buf = NULL; size_t buflen;
+		CHECK_MALLOC_DO( fd_msg_dump_treeview(&buf, &buflen, NULL, *answer, NULL, 0, 1), );
+		TRACE_DEBUG(INFO, "Received the following problematic STA message, discarding anyway: %s", buf ?: "<error>");
+		free(buf);
+		
+		fd_msg_free(*answer);
+		*answer = NULL;
+	}
+	return;
+}
+
+static int acct_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+{
+	struct session * sess;
+	struct sess_state * st = NULL, stloc;
+	struct avp *avp, *next;
+	struct avp_hdr *ahdr, *oh, *or;
+	os0_t sid = NULL;
+	size_t sidlen;
+	
+	TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
+	CHECK_PARAMS(cs);
+	
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
+	if (sess) {
+		CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, sess, &st ) );
+		CHECK_FCT( fd_sess_getsid(sess, &sid, &sidlen) );
+	}
+	
+	if (!st) {
+		TRACE_DEBUG(INFO, "Received an ACA without corresponding session information, cannot translate to RADIUS");
+		return EINVAL;
+	}
+	
+	/* Free the state */
+	memcpy(&stloc, st, sizeof(struct sess_state));
+	free(st);
+	st = &stloc;
+	
+	/* Search these AVPs first */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Host, &avp) );
+	CHECK_FCT( fd_msg_avp_hdr ( avp, &oh ) );
+
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Realm, &avp) );
+	CHECK_FCT( fd_msg_avp_hdr ( avp, &or ) );
+
+	/* Check the Diameter error code */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Result_Code, &avp) );
+	ASSERT( avp ); /* otherwise the message should have been discarded a lot earlier because of ABNF */
+	CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+	switch (ahdr->avp_value->u32) {
+		case ER_DIAMETER_SUCCESS:
+		case ER_DIAMETER_LIMITED_SUCCESS:
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCOUNTING_RESPONSE;
+			break;
+		
+		default:
+			fd_log_debug("[acct.rgwx] Received Diameter answer with error code '%d' from server '%.*s', session %.*s, not translating into Accounting-Response",
+					ahdr->avp_value->u32, 
+					(int)oh->avp_value->os.len, oh->avp_value->os.data,
+					(int)sidlen, sid);
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Message, &avp) );
+			if (avp) {
+				CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+				fd_log_debug("[acct.rgwx]   Error-Message content: '%.*s'",
+						(int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
+			}
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Reporting_Host, &avp) );
+			if (avp) {
+				CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+				fd_log_debug("[acct.rgwx]   Error-Reporting-Host: '%.*s'",
+						(int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
+			}
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Failed_AVP, &avp) );
+			if (avp) {
+				fd_log_debug("[acct.rgwx]   Failed-AVP was included in the message.");
+				/* Dump its content ? */
+			}
+			
+			/* Now, destroy the Diameter message, since we know it is not converted to RADIUS */
+			CHECK_FCT( fd_msg_free(*diam_ans) );
+			*diam_ans = NULL;
+
+			return -1;
+	}
+	/* Remove this Result-Code avp */
+	CHECK_FCT( fd_msg_free( avp ) );
+	
+	/* If it was a response to a STOP record, we must send an STR for this session */
+	if (st->send_str) {
+		struct msg * str = NULL;
+		struct msg_hdr * hdr = NULL;
+		DiamId_t fqdn;
+		size_t fqdn_len;
+		DiamId_t realm;
+		size_t realm_len;
+		union avp_value avp_val;
+		
+		/* Create a new STR message */
+		CHECK_FCT(  fd_msg_new ( cs->dict.Session_Termination_Request, MSGFL_ALLOC_ETEID, &str )  );
+		
+		/* Set the application-id to the auth application if available, accounting otherwise (not sure what is actually expected...) */
+		CHECK_FCT( fd_msg_hdr ( str, &hdr ) );
+		hdr->msg_appl = st->auth_appl ?: AI_ACCT;
+		
+		/* Add the Session-Id AVP as first AVP */
+		CHECK_FCT( fd_msg_avp_new (  cs->dict.Session_Id, 0, &avp ) );
+		avp_val.os.data = sid;
+		avp_val.os.len = sidlen;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_FIRST_CHILD, avp) );
+		CHECK_FCT( fd_sess_ref_msg(sess) );
+
+		/* Add the Destination-Realm as next AVP */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, or->avp_value ) );
+		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+
+		/* Get information on the NAS */
+		CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdn_len, &realm, &realm_len) );
+
+		/* Add the Origin-Host as next AVP */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Host, 0, &avp ) );
+		memset(&avp_val, 0, sizeof(avp_val));
+		avp_val.os.data = (unsigned char *)fqdn;
+		avp_val.os.len = fqdn_len;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+
+		/* Add the Origin-Realm as next AVP */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Realm, 0, &avp ) );
+		memset(&avp_val, 0, sizeof(avp_val));
+		avp_val.os.data = (unsigned char *)realm;
+		avp_val.os.len = realm_len;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+		
+		/* Auth-Application-Id -- if we did not get it from our Class attribute, we just set "0" */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Application_Id, 0, &avp ) );
+		avp_val.u32 = st->auth_appl;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+		
+		/* Termination-Cause */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Termination_Cause, 0, &avp ) );
+		avp_val.u32 = st->term_cause;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
+		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+		
+		/* Send this message */
+		CHECK_FCT( fd_msg_send ( &str, handle_sta, cs ) );
+	}
+	
+	/* 
+		No attributes should be found in
+		   Accounting-Response packets except Proxy-State and possibly Vendor-
+		   Specific.
+	*/
+
+	/* Now loop in the list of AVPs and convert those that we know how */
+	CHECK_FCT( fd_msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) );
+	
+	while (next) {
+		int handled = 1;
+		avp = next;
+		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL) );
+		
+		CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+		
+		if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+			switch (ahdr->avp_code) {
+				/* Based on RFC4005, sec 3.10 */
+				case DIAM_ATTR_SESSION_ID:
+				case DIAM_ATTR_ORIGIN_HOST:
+				case DIAM_ATTR_ORIGIN_REALM:
+				case DIAM_ATTR_ACCOUNTING_RECORD_TYPE:
+				case DIAM_ATTR_ACCOUNTING_RECORD_NUMBER:
+				case DIAM_ATTR_ACCT_APPLICATION_ID:
+				case DIAM_ATTR_VENDOR_SPECIFIC_APPLICATION_ID:
+				case DIAM_ATTR_USER_NAME:
+				case DIAM_ATTR_ACCOUNTING_SUB_SESSION_ID:
+				case DIAM_ATTR_ACCT_SESSION_ID:
+				case DIAM_ATTR_ACCT_MULTI_SESSION_ID:
+				case DIAM_ATTR_EVENT_TIMESTAMP:
+				case DIAM_ATTR_ORIGIN_AAA_PROTOCOL:
+				case DIAM_ATTR_ORIGIN_STATE_ID:
+				case DIAM_ATTR_NAS_IDENTIFIER:
+				case DIAM_ATTR_NAS_IP_ADDRESS:
+				case DIAM_ATTR_NAS_IPV6_ADDRESS:
+				case DIAM_ATTR_NAS_PORT:
+				case DIAM_ATTR_NAS_PORT_ID:
+				case DIAM_ATTR_NAS_PORT_TYPE:
+				case DIAM_ATTR_SERVICE_TYPE:
+				case DIAM_ATTR_TERMINATION_CAUSE:
+				case DIAM_ATTR_ACCOUNTING_REALTIME_REQUIRED:
+				case DIAM_ATTR_ACCT_INTERIM_INTERVAL:
+				case DIAM_ATTR_CLASS:
+					/* We just remove these AVP, they are not expected in RADIUS client */
+					break;
+					
+				default:
+					/* Leave the AVP in the message for further treatment */
+					handled = 0;
+			}
+		} else {
+			/* Vendor-specific AVPs */
+			switch (ahdr->avp_vendor) {
+				
+				default: /* unknown vendor */
+					handled = 0;
+			}
+		}
+		
+		if (handled) {
+			CHECK_FCT( fd_msg_free( avp ) );
+		}
+	}
+	
+	/*
+	      The Authenticator field in an Accounting-Response packet is called
+	      the Response Authenticator, and contains a one-way MD5 hash
+	      calculated over a stream of octets consisting of the Accounting-
+	      Response Code, Identifier, Length, the Request Authenticator field
+	      from the Accounting-Request packet being replied to, and the
+	      response attributes if any, followed by the shared secret.  The
+	      resulting 16 octet MD5 hash value is stored in the Authenticator
+	      field of the Accounting-Response packet.
+	      
+	      -- done in radius_msg_finish_srv 
+	*/
+
+	return 0;
+}
+
+/* The exported symbol */
+struct rgw_api rgwp_descriptor = {
+	.rgwp_name       = "acct",
+	.rgwp_conf_parse = acct_conf_parse,
+	.rgwp_conf_free  = acct_conf_free,
+	.rgwp_rad_req    = acct_rad_req,
+	.rgwp_diam_ans   = acct_diam_ans
+};	
diff --git a/extensions/app_radgw/rgwx_auth.c b/extensions/app_radgw/rgwx_auth.c
new file mode 100644
index 0000000..f78b97e
--- /dev/null
+++ b/extensions/app_radgw/rgwx_auth.c
@@ -0,0 +1,1961 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* RADIUS Access-Request messages translation plugin */
+
+#include "rgw_common.h"
+
+/* Attributes missing from radius.h */
+#define RADIUS_ATTR_CHAP_PASSWORD	3
+#define RADIUS_ATTR_ARAP_PASSWORD	70
+
+/* Other constants we use */
+#define AI_NASREQ		1	/* Diameter NASREQ */
+#define AI_EAP			5	/* Diameter EAP application */
+#define CC_AA			265	/* AAR */
+#define CC_DIAMETER_EAP		268	/* DER */
+#define ACV_ART_AUTHORIZE_AUTHENTICATE	3	/* AUTHORIZE_AUTHENTICATE */
+#define ACV_OAP_RADIUS			1	/* RADIUS */
+#define ACV_ASS_STATE_MAINTAINED	0	/* STATE_MAINTAINED */
+#define ACV_ASS_NO_STATE_MAINTAINED	1	/* NO_STATE_MAINTAINED */
+
+/* The state we keep for this plugin */
+struct rgwp_config {
+	struct {
+		struct dict_object * ARAP_Password;		/* ARAP-Password */
+		struct dict_object * ARAP_Security;		/* ARAP-Security */
+		struct dict_object * ARAP_Security_Data;	/* ARAP-Security-Data */
+		struct dict_object * Auth_Application_Id;	/* Auth-Application-Id */
+		struct dict_object * Auth_Request_Type;		/* Auth-Request-Type */
+		struct dict_object * Authorization_Lifetime;	/* Authorization-Lifetime */
+		struct dict_object * Callback_Number;		/* Callback-Number */
+		struct dict_object * Called_Station_Id;		/* Called-Station-Id */
+		struct dict_object * Calling_Station_Id;	/* Calling-Station-Id */
+		struct dict_object * CHAP_Algorithm;		/* CHAP-Algorithm */
+		struct dict_object * CHAP_Auth;			/* CHAP-Auth */
+		struct dict_object * CHAP_Challenge;		/* CHAP-Challenge */
+		struct dict_object * CHAP_Ident;		/* CHAP-Ident */
+		struct dict_object * CHAP_Response;		/* CHAP-Response */
+		struct dict_object * Destination_Host;		/* Destination-Host */
+		struct dict_object * Destination_Realm;		/* Destination-Realm */
+		struct dict_object * Connect_Info;		/* Connect-Info */
+		struct dict_object * EAP_Payload;		/* EAP-Payload */
+		struct dict_object * Error_Message;		/* Error-Message */
+		struct dict_object * Error_Reporting_Host;	/* Error-Reporting-Host */
+		struct dict_object * Failed_AVP;		/* Failed-AVP */
+		struct dict_object * Framed_Compression;	/* Framed-Compression */
+		struct dict_object * Framed_IP_Address;		/* Framed-IP-Address */
+		struct dict_object * Framed_IP_Netmask;		/* Framed-IP-Netmask */
+		struct dict_object * Framed_Interface_Id;	/* Framed-Interface-Id */
+		struct dict_object * Framed_IPv6_Prefix;	/* Framed-IPv6-Prefix */
+		struct dict_object * Framed_MTU;		/* Framed-MTU */
+		struct dict_object * Framed_Protocol;		/* Framed-Protocol */
+		struct dict_object * Login_IP_Host;		/* Login-IP-Host */
+		struct dict_object * Login_IPv6_Host;		/* Login-IPv6-Host */
+		struct dict_object * Login_LAT_Group;		/* Login-LAT-Group */
+		struct dict_object * Login_LAT_Node;		/* Login-LAT-Node */
+		struct dict_object * Login_LAT_Port;		/* Login-LAT-Port */
+		struct dict_object * Login_LAT_Service;		/* Login-LAT-Service */
+		struct dict_object * NAS_Identifier;		/* NAS-Identifier */
+		struct dict_object * NAS_IP_Address;		/* NAS-IP-Address */
+		struct dict_object * NAS_IPv6_Address;		/* NAS-IPv6-Address */
+		struct dict_object * NAS_Port;			/* NAS-Port */
+		struct dict_object * NAS_Port_Id;		/* NAS-Port-Id */
+		struct dict_object * NAS_Port_Type;		/* NAS-Port-Type */
+		struct dict_object * Origin_AAA_Protocol;	/* Origin-AAA-Protocol */
+		struct dict_object * Origin_Host;		/* Origin-Host */
+		struct dict_object * Origin_Realm;		/* Origin-Realm */
+		struct dict_object * Originating_Line_Info;	/* Originating-Line-Info */
+		struct dict_object * Port_Limit;		/* Port-Limit */
+		struct dict_object * Re_Auth_Request_Type;	/* Re-Auth-Request-Type */
+		struct dict_object * Result_Code;		/* Result-Code */
+		struct dict_object * Service_Type;		/* Service-Type */
+		struct dict_object * Session_Id;		/* Session-Id */
+		struct dict_object * Session_Timeout;		/* Session-Timeout */
+		struct dict_object * State;			/* State */
+		struct dict_object * Tunneling;			/* Tunneling */
+		struct dict_object * Tunnel_Type;		/* Tunnel-Type */
+		struct dict_object * Tunnel_Medium_Type;	/* Tunnel-Medium-Type */
+		struct dict_object * Tunnel_Client_Endpoint;	/* Tunnel-Client-Endpoint */
+		struct dict_object * Tunnel_Server_Endpoint;	/* Tunnel-Server-Endpoint */
+		struct dict_object * Tunnel_Private_Group_Id;	/* Tunnel-Private-Group-Id */
+		struct dict_object * Tunnel_Preference;		/* Tunnel-Preference */
+		struct dict_object * Tunnel_Client_Auth_Id;	/* Tunnel-Client-Auth-Id */
+		struct dict_object * Tunnel_Server_Auth_Id;	/* Tunnel-Server-Auth-Id */
+		struct dict_object * User_Name;			/* User-Name */
+		struct dict_object * User_Password;		/* User-Password */
+		
+	} dict; /* cache of the dictionary objects we use */
+	struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */
+	char * confstr;
+	
+	int ignore_nai;
+};
+
+struct sess_state {
+	uint8_t req_auth[16];
+};
+
+
+/* Initialize the plugin */
+static int auth_conf_parse(char * confstr, struct rgwp_config ** state)
+{
+	struct rgwp_config * new;
+	struct dict_object * app;
+	
+	TRACE_ENTRY("%p %p", confstr, state);
+	CHECK_PARAMS( state );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
+	memset(new, 0, sizeof(struct rgwp_config));
+	
+	CHECK_FCT( fd_sess_handler_create( &new->sess_hdl, (void *)free, NULL, NULL ) );
+	new->confstr = confstr;
+	
+	if (confstr && strstr(confstr, "nonai"))
+		new->ignore_nai = 1;
+	
+	/* Resolve all dictionary objects we use */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Password", &new->dict.ARAP_Password, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security", &new->dict.ARAP_Security, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security-Data", &new->dict.ARAP_Security_Data, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &new->dict.Auth_Application_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Request-Type", &new->dict.Auth_Request_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Authorization-Lifetime", &new->dict.Authorization_Lifetime, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Number", &new->dict.Callback_Number, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Called-Station-Id", &new->dict.Called_Station_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Calling-Station-Id", &new->dict.Calling_Station_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Algorithm", &new->dict.CHAP_Algorithm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Auth", &new->dict.CHAP_Auth, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Challenge", &new->dict.CHAP_Challenge, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Ident", &new->dict.CHAP_Ident, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Response", &new->dict.CHAP_Response, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Connect-Info", &new->dict.Connect_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &new->dict.Destination_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &new->dict.Destination_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "EAP-Payload", &new->dict.EAP_Payload, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &new->dict.Error_Message, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &new->dict.Error_Reporting_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &new->dict.Failed_AVP, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Compression", &new->dict.Framed_Compression, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Address", &new->dict.Framed_IP_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Netmask", &new->dict.Framed_IP_Netmask, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Interface-Id", &new->dict.Framed_Interface_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Prefix", &new->dict.Framed_IPv6_Prefix, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-MTU", &new->dict.Framed_MTU, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Protocol", &new->dict.Framed_Protocol, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IP-Host", &new->dict.Login_IP_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IPv6-Host", &new->dict.Login_IPv6_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Group", &new->dict.Login_LAT_Group, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Node", &new->dict.Login_LAT_Node, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Port", &new->dict.Login_LAT_Port, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Service", &new->dict.Login_LAT_Service, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Identifier", &new->dict.NAS_Identifier, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IP-Address", &new->dict.NAS_IP_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IPv6-Address", &new->dict.NAS_IPv6_Address, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port", &new->dict.NAS_Port, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Id", &new->dict.NAS_Port_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Type", &new->dict.NAS_Port_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-AAA-Protocol", &new->dict.Origin_AAA_Protocol, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &new->dict.Origin_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &new->dict.Origin_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Originating-Line-Info", &new->dict.Originating_Line_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Port-Limit", &new->dict.Port_Limit, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Re-Auth-Request-Type", &new->dict.Re_Auth_Request_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &new->dict.Result_Code, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Service-Type", &new->dict.Service_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Timeout", &new->dict.Session_Timeout, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "State", &new->dict.State, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunneling", &new->dict.Tunneling, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Type", &new->dict.Tunnel_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Medium-Type", &new->dict.Tunnel_Medium_Type, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Endpoint", &new->dict.Tunnel_Client_Endpoint, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Endpoint", &new->dict.Tunnel_Server_Endpoint, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Private-Group-Id", &new->dict.Tunnel_Private_Group_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Preference", &new->dict.Tunnel_Preference, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Auth-Id", &new->dict.Tunnel_Client_Auth_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Auth-Id", &new->dict.Tunnel_Server_Auth_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Password", &new->dict.User_Password, ENOENT) );
+	
+	/* This plugin provides the following Diameter authentication applications support: */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Network Access Server Application", &app, ENOENT) );
+	CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
+
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Extensible Authentication Protocol (EAP) Application", &app, ENOENT) );
+	CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
+	
+	*state = new;
+	return 0;
+}
+
+/* deinitialize */
+static void auth_conf_free(struct rgwp_config * state)
+{
+	TRACE_ENTRY("%p", state);
+	CHECK_PARAMS_DO( state, return );
+	CHECK_FCT_DO( fd_sess_handler_destroy( &state->sess_hdl, NULL ),  );
+	free(state);
+	return;
+}
+
+/* Handle an incoming RADIUS request */
+static int auth_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+{
+	int idx;
+	int got_id = 0;
+	int got_mac = 0;
+	int got_passwd = 0;
+	int got_eap = 0;
+	int got_empty_eap = 0;
+	const char * prefix = "Diameter/";
+	size_t pref_len;
+	os0_t dh = NULL;
+	size_t dh_len = 0;
+	os0_t dr = NULL;
+	size_t dr_len = 0;
+	os0_t si = NULL;
+	size_t si_len = 0;
+	os0_t un = NULL;
+	size_t un_len = 0;
+	size_t nattr_used = 0;
+	struct avp ** avp_tun = NULL, *avp = NULL;
+	union avp_value value;
+	struct session * sess;
+	
+	TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
+	CHECK_PARAMS(cs && rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
+	
+	pref_len = strlen(prefix);
+	
+	/*
+	   Guidelines:
+	     http://tools.ietf.org/html/rfc4005#section-9.1
+	     http://tools.ietf.org/html/rfc4072#section-6.1
+
+	   When a Translation Agent receives a RADIUS message, the following
+	   steps should be taken:
+
+	      -  If a Message-Authenticator attribute is present, the value MUST
+        	 be checked but not included in the Diameter message.  If it is
+        	 incorrect, the RADIUS message should be silently discarded.
+        	 The gateway system SHOULD generate and include a Message-
+        	 Authenticator in returned RADIUS responses.
+	             -> done in rgw_msg_auth_check
+
+	      -  The transport address of the sender MUST be checked against the
+        	 NAS identifying attributes.  See the description of NAS-
+        	 Identifier and NAS-IP-Address below.
+		     -> done in rgw_clients_check_origin
+
+	      -  The Translation Agent must maintain transaction state
+        	 information relevant to the RADIUS request, such as the
+        	 Identifier field in the RADIUS header, any existing RADIUS
+        	 Proxy-State attribute, and the source IP address and port
+        	 number of the UDP packet.  These may be maintained locally in a
+        	 state table or saved in a Proxy-Info AVP group.  A Diameter
+        	 Session-Id AVP value must be created using a session state
+        	 mapping mechanism.
+		     -> Identifier, source and port are saved along with the request,
+		        and associated with the session state.
+		     -> sub_echo_drop should handle the Proxy-State attribute (conf issue)
+
+	      -  The Diameter Origin-Host and Origin-Realm AVPs MUST be created
+        	 and added by using the information from an FQDN corresponding
+        	 to the NAS-IP-Address attribute (preferred if available),
+        	 and/or to the NAS-Identifier attribute.  (Note that the RADIUS
+        	 NAS-Identifier is not required to be an FQDN.)
+		     -> done in rgw_clients_create_origin.
+
+	      -  The response MUST have an Origin-AAA-Protocol AVP added,
+        	 indicating the protocol of origin of the message.
+		     -> what "response" ??? Added to the AAR / DER in this function.
+
+	      -  The Proxy-Info group SHOULD be added, with the local server's
+        	 identity specified in the Proxy-Host AVP.  This should ensure
+        	 that the response is returned to this system.
+		     -> We don't need this, answer is always routed here anyway.
+		     
+	      For EAP:
+	      
+	      o  RADIUS EAP-Message attribute(s) are translated to a Diameter
+		 EAP-Payload AVP.  If multiple RADIUS EAP-Message attributes are
+		 present, they are concatenated and translated to a single Diameter
+		 EAP-Payload AVP.
+		     -> concatenation done by radius_msg_get_eap
+
+	      -> the remaining is specific conversion rules
+	*/
+	
+	/* Check basic information is there, and also retrieve some attribute information */
+	for (idx = 0; idx < rad_req->attr_used; idx++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+		uint8_t * attr_val = (uint8_t *)(attr + 1);
+		size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
+		
+		switch (attr->type) {
+			case RADIUS_ATTR_NAS_IP_ADDRESS:
+			case RADIUS_ATTR_NAS_IDENTIFIER:
+			case RADIUS_ATTR_NAS_IPV6_ADDRESS:
+				got_id = 1;
+				break;
+			case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
+				got_mac = 1;
+				break;
+			case RADIUS_ATTR_EAP_MESSAGE:
+				got_eap = 1;
+				if (attr->length == 2)
+					got_empty_eap = 1;
+				break;
+			case RADIUS_ATTR_USER_PASSWORD:
+			case RADIUS_ATTR_CHAP_PASSWORD:
+			case RADIUS_ATTR_ARAP_PASSWORD:
+				got_passwd += 1;
+				break;
+				
+			/* Is there a State attribute with prefix "Diameter/" in the message? (in that case: Diameter/Destination-Host/Destination-Realm/Session-Id) */
+			/* NOTE: RFC4005 says "Origin-Host" here, but it's not coherent with the rules for answers. Destination-Host makes more sense */
+			case RADIUS_ATTR_STATE:
+				if ((attr_len > pref_len + 5 /* for the '/'s and non empty strings */ ) 
+					&& ! memcmp(attr_val, prefix, pref_len)) {
+					int i, start;
+
+					TRACE_DEBUG(ANNOYING, "Found a State attribute with '%s' prefix (attr #%d).", prefix, idx);
+
+					/* Now parse the value and check its content is valid. Unfortunately we cannot use strchr here since strings are not \0-terminated */
+
+					i = start = pref_len;
+					dh = attr_val + i;
+					for (; (i < attr_len - 2) && (attr_val[i] != '/'); i++) /* loop */;
+					if ( i >= attr_len - 2 ) continue; /* the attribute format is not good */
+					dh_len = i - start;
+
+					start = ++i;
+					dr = attr_val + i;
+					for (; (i < attr_len - 1) && (attr_val[i] != '/'); i++) /* loop */;
+					if ( i >= attr_len - 1 ) continue; /* the attribute format is not good */
+					dr_len = i - start;
+
+					i++;
+					si = attr_val + i;
+					si_len = attr_len - i;
+
+					TRACE_DEBUG(ANNOYING, "Attribute parsed successfully: DH:'%.*s' DR:'%.*s' SI:'%.*s'.", (int)dh_len, dh, (int)dr_len, dr, (int)si_len, si);
+					/* Remove from the message */
+					for (i = idx + 1; i < rad_req->attr_used; i++)
+						rad_req->attr_pos[i - 1] = rad_req->attr_pos[i];
+					rad_req->attr_used -= 1;
+					idx--;
+				}
+				break;
+		
+			case RADIUS_ATTR_USER_NAME:
+				TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", (int)attr_len, attr_len ? (char *)attr_val : "");
+				un = attr_val;
+				un_len = attr_len;
+				break;
+			
+		}
+	}
+	if (!got_id) {
+		TRACE_DEBUG(INFO, "RADIUS Access-Request did not contain a NAS IP or Identifier attribute, reject.");
+		return EINVAL;
+	}
+	/* [Note 1] An Access-Request that contains either a User-Password or
+	   CHAP-Password or ARAP-Password or one or more EAP-Message attributes
+	   MUST NOT contain more than one type of those four attributes.  If it
+	   does not contain any of those four attributes, it SHOULD contain a
+	   Message-Authenticator.  If any packet type contains an EAP-Message
+	   attribute it MUST also contain a Message-Authenticator.  A RADIUS
+	   server receiving an Access-Request not containing any of those four
+	   attributes and also not containing a Message-Authenticator attribute
+	   SHOULD silently discard it.  */
+	if (((got_eap + got_passwd) > 1) || (got_eap && !got_mac) || (!got_eap && !got_passwd && !got_mac)) {
+		TRACE_DEBUG(INFO, "RADIUS Access-Request not conform to RFC3579 sec 3.3 note 1, discard.");
+		return EINVAL;
+	}
+	
+	
+	
+	/*
+	      -  If the RADIUS request contained a State attribute and the
+        	 prefix of the data is "Diameter/", the data following the
+        	 prefix contains the Diameter Origin-Host/Origin-Realm/Session-
+        	 Id.  If no such attributes are present and the RADIUS command
+        	 is an Access-Request, a new Session-Id is created.  The
+        	 Session-Id is included in the Session-Id AVP.
+	*/
+	
+	/* Add the Destination-Realm AVP */
+	CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
+	if (dr) {
+		value.os.data = (unsigned char *)dr;
+		value.os.len = dr_len;
+	} else {
+		int i = 0;
+		if (un && ! cs->ignore_nai) {
+			/* Is there an '@' in the user name? We don't care for decorated NAI here */
+			for (i = un_len - 2; i > 0; i--) {
+				if (un[i] == '@') {
+					i++;
+					break;
+				}
+			}
+		}
+		if (i <= 0) {
+			/* Not found in the User-Name => we use the local domain of this gateway */
+			value.os.data = (uint8_t *)fd_g_config->cnf_diamrlm;
+			value.os.len  = fd_g_config->cnf_diamrlm_len;
+		} else {
+			value.os.data = un + i;
+			value.os.len  = un_len - i;
+		}
+	}
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+	
+	/* Add the Destination-Host if found */
+	if (dh) {
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Host, 0, &avp ) );
+		value.os.data = (unsigned char *)dh;
+		value.os.len = dh_len;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+	}
+	
+	/* Create the session if it is not already done */
+	{
+		os0_t sess_str = NULL;
+		size_t sess_strlen;
+		
+		if (si_len) {
+			/* We already have the Session-Id, just use it */
+			CHECK_FCT( fd_sess_fromsid_msg ( si, si_len, &sess, NULL) );
+		} else {
+			/* Create a new Session-Id string */
+			
+			DiamId_t fqdn;
+			size_t fqdnlen;
+			DiamId_t realm;
+			size_t realmlen;
+			
+			/* Get information on the RADIUS client */
+			CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdnlen, &realm, &realmlen) );
+			
+			/* If we have a user name, create the new session with it */
+			if (un) {
+				int len;
+				/* If not found, create a new Session-Id. Our format is: {fqdn;hi32;lo32;username;diamid} */
+				CHECK_MALLOC( sess_str = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) );
+				len = sprintf((char *)sess_str, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
+				CHECK_FCT( fd_sess_new(&sess, fqdn, fqdnlen, sess_str, len) );
+				free(sess_str);
+			} else {
+				/* We don't have enough information to create the Session-Id, the RADIUS message is probably invalid */
+				TRACE_DEBUG(INFO, "RADIUS Access-Request does not contain a User-Name attribute, rejecting.");
+				return EINVAL;
+			}	
+		}
+		
+		/* Now, add the Session-Id AVP at beginning of Diameter message */
+		CHECK_FCT( fd_sess_getsid(sess, &sess_str, &sess_strlen) );
+		TRACE_DEBUG(FULL, "[auth.rgwx] Translating new message for session '%s'...", sess_str);
+		
+		/* Now add this session in the message */
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
+		value.os.data = sess_str;
+		value.os.len = sess_strlen;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+		CHECK_FCT( fd_msg_sess_set(*diam_fw, sess) );
+	}
+	
+	
+	/* Add the appropriate command code & Auth-Application-Id */
+	{
+		struct msg_hdr * header = NULL;
+		CHECK_FCT( fd_msg_hdr ( *diam_fw, &header ) );
+		header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
+		if (got_eap) {
+			header->msg_code = CC_DIAMETER_EAP;
+			header->msg_appl = AI_EAP;
+		} else {
+			header->msg_code = CC_AA;
+			header->msg_appl = AI_NASREQ;
+		}
+		
+		/* Add the Auth-Application-Id */
+		{
+			CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Application_Id, 0, &avp ) );
+			value.i32 = header->msg_appl;
+			CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+			CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+		}
+	}
+	
+	/*  	The type of request is identified through the Auth-Request-Type AVP
+		[BASE].  The recommended value for most RADIUS interoperabily
+		situations is AUTHORIZE_AUTHENTICATE. */
+	
+	/* Add Auth-Request-Type AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Request_Type, 0, &avp ) );
+		value.i32 = ACV_ART_AUTHORIZE_AUTHENTICATE;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	/* Add Origin-AAA-Protocol AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_AAA_Protocol, 0, &avp ) );
+		value.i32 = ACV_OAP_RADIUS;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	/* Convert the EAP payload (concat RADIUS attributes) */
+	if (got_eap) {
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.EAP_Payload, 0, &avp ) );
+		
+		/*    o  An empty RADIUS EAP-Message attribute (with length 2) signifies
+			 EAP-Start, and it is translated to an empty EAP-Payload AVP. */
+		if (got_empty_eap) {
+			value.os.len = 0;
+			value.os.data = (uint8_t *)"";
+		} else {
+			CHECK_MALLOC( value.os.data = radius_msg_get_eap(rad_req, &value.os.len) );
+		}
+		
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	/* Tunnel AVPs need some preparation */
+	/* Convert the attributes one by one */
+	for (idx = 0; idx < rad_req->attr_used; idx++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+
+		switch (attr->type) {
+			
+			/* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
+			#define CONV2DIAM_STR( _dictobj_ )	\
+				CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) );			\
+				/* Create the AVP with the specified dictionary model */			\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				value.os.len = attr->length - sizeof(struct radius_attr_hdr);			\
+				value.os.data = (os0_t)(attr + 1);						\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				/* Add the AVP in the Diameter message. */					\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+			/* Same thing, for scalar AVPs of 32 bits */
+			#define CONV2DIAM_32B( _dictobj_ )	\
+				CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u32  = (v[0] << 24)						\
+					           | (v[1] << 16)						\
+					           | (v[2] <<  8)						\
+					           |  v[3] ;							\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+			/* And the 64b version */
+			#define CONV2DIAM_64B( _dictobj_ )	\
+				CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint64_t) );\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u64  = ((uint64_t)(v[0]) << 56)					\
+					           | ((uint64_t)(v[1]) << 48)					\
+					           | ((uint64_t)(v[2]) << 40)					\
+					           | ((uint64_t)(v[3]) << 32)					\
+					           | ((uint64_t)(v[4]) << 24)					\
+					           | ((uint64_t)(v[5]) << 16)					\
+					           | ((uint64_t)(v[6]) <<  8)					\
+					           |  (uint64_t)(v[7]) ;					\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+		/* RFC 2865 */	
+			/*
+			      -  The Destination-Realm AVP is created from the information found
+        			 in the RADIUS User-Name attribute.
+				 	-> done in rgw_clients_create_origin
+			*/
+			case RADIUS_ATTR_USER_NAME:
+				CONV2DIAM_STR( User_Name );
+				break;
+				
+			/*
+			      -  If the RADIUS User-Password attribute is present, the password
+        			 must be unencrypted by using the link's RADIUS shared secret.
+        			 The unencrypted value must be forwarded in a User-Password AVP
+        			 using Diameter security.
+			*/
+			case RADIUS_ATTR_USER_PASSWORD:
+				if ((attr->length - 2) % 16) {
+					TRACE_DEBUG(INFO, "Invalid length of User-Password attribute: %hhd", attr->length);
+					return EINVAL;
+				}
+				{
+					/* Decipher following this logic (refers to rfc2865#section-5.2 )
+					   b1 = MD5(S + RA)	p1 = c(1) xor b1
+					   b2 = MD5(S + c(1))   p2 = c(2) xor b2
+					   ...
+					*/
+					
+					uint8_t *ciph = (uint8_t *)(attr+1); 	/* c(i) */
+					size_t ciph_len = attr->length - 2;
+					uint8_t deciph[128];			/* pi */
+					size_t deciph_len = 0;
+					uint8_t * secret;			/* S */
+					size_t secret_len;
+					uint8_t hash[16];			/* b(i) */
+					const uint8_t *addr[2];
+					size_t len[2];
+					
+					/* Retrieve the shared secret */
+					CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
+					
+					/* Initial b1 = MD5(S + RA) */
+					addr[0] = secret;
+					len[0] = secret_len;
+					addr[1] = rad_req->hdr->authenticator;
+					len[1] = 16;
+					md5_vector(2, addr, len, hash);
+					
+					/* loop */
+					while (deciph_len < ciph_len) {
+						int i;
+						/* pi = c(i) xor bi */
+						for (i = 0; i < 16; i++)
+							deciph[deciph_len + i] = ciph[deciph_len + i] ^ hash[i];
+							/* do we have to remove the padding '\0's ? */
+						
+						/* b(i+1) = MD5(S + c(i) */
+						addr[1] = ciph + deciph_len;
+						md5_vector(2, addr, len, hash);
+						
+						deciph_len += 16;
+					}
+					
+					/* Now save this value in the AVP */
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.User_Password, 0, &avp ) );
+					value.os.data = &deciph[0];
+					value.os.len  = deciph_len;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+				}
+				break;
+				
+
+			/*
+			      -  If the RADIUS CHAP-Password attribute is present, the Ident and
+        			 Data portion of the attribute are used to create the CHAP-Auth
+        			 grouped AVP.
+			*/
+			case RADIUS_ATTR_CHAP_PASSWORD:
+				CHECK_PARAMS( attr->length == 19 /* RFC 2865 */);
+				{
+					uint8_t * c = (uint8_t *)(attr + 1);
+					struct avp * chap_auth;
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Auth, 0, &chap_auth ) );
+					CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, chap_auth) );
+
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Algorithm, 0, &avp ) );
+					value.u32 = 5; /* The only value defined currently... */
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
+					
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Ident, 0, &avp ) );
+					value.os.data = c;
+					value.os.len = 1;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
+					
+					c++;
+					
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Response, 0, &avp ) );
+					value.os.data = c;
+					value.os.len = attr->length - 3;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
+				}
+				break;
+				
+			case RADIUS_ATTR_NAS_IP_ADDRESS:
+				CONV2DIAM_STR( NAS_IP_Address );
+				break;
+				
+			case RADIUS_ATTR_NAS_PORT:
+				CONV2DIAM_32B( NAS_Port );
+				break;
+				
+			case RADIUS_ATTR_SERVICE_TYPE:
+				CONV2DIAM_32B( Service_Type );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_PROTOCOL:
+				CONV2DIAM_32B( Framed_Protocol );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IP_ADDRESS:
+				CONV2DIAM_STR( Framed_IP_Address );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IP_NETMASK:
+				CONV2DIAM_STR( Framed_IP_Netmask );
+				break;
+				
+			/* Framed-Routing never present in an Access-Request */
+			/* Filter-Id never present in an Access-Request */
+				
+			case RADIUS_ATTR_FRAMED_MTU:
+				CONV2DIAM_32B( Framed_MTU );
+				break;
+			
+			case RADIUS_ATTR_FRAMED_COMPRESSION:
+				CONV2DIAM_32B( Framed_Compression );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_IP_HOST:
+				CONV2DIAM_STR( Login_IP_Host );
+				break;
+				
+			/* Login-Service never present in an Access-Request */
+			/* Login-TCP-Port never present in an Access-Request */
+			/* Reply-Message never present in an Access-Request */
+			
+			case RADIUS_ATTR_CALLBACK_NUMBER:
+				CONV2DIAM_STR( Callback_Number );
+				break;
+				
+			/* Callback-Id never present in an Access-Request */
+			/* Framed-Route never present in an Access-Request */
+			/* Framed-IPX-Network never present in an Access-Request */
+				
+			case RADIUS_ATTR_STATE:
+				CONV2DIAM_STR( State );
+				break;
+			
+			/* Class never present in an Access-Request */
+				
+			case RADIUS_ATTR_VENDOR_SPECIFIC:
+				/* RFC 4005, Section 9.6 : 
+					   Systems that don't have vendor format knowledge MAY discard such
+					   attributes without knowing a suitable translation.
+					   
+					   [conversion rule in 9.6.2]
+				 */
+				if (attr->length >= 6) {
+					uint32_t vendor_id;
+					uint8_t * c = (uint8_t *)(attr + 1);
+					
+					vendor_id = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];
+					c += 4;
+					
+					switch (vendor_id) {
+						
+						/* For the vendors we KNOW they follow the VSA recommended format, we convert following the rules of RFC4005 (9.6.2) */
+						case RADIUS_VENDOR_ID_MICROSOFT : /* RFC 2548 */
+						/* other vendors ? */
+						{
+							size_t left;
+							struct radius_attr_vendor *vtlv;
+							
+							left = attr->length - 6;
+							vtlv = (struct radius_attr_vendor *)c;
+						
+							while ((left >= 2) && (vtlv->vendor_length <= left)) {
+								/* Search our dictionary for corresponding Vendor's AVP */
+								struct dict_avp_request req;
+								struct dict_object * avp_model = NULL;
+								memset(&req, 0, sizeof(struct dict_avp_request));
+								req.avp_vendor = vendor_id;
+								req.avp_code = vtlv->vendor_type;
+								
+								CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &req, &avp_model, 0) );
+								if (!avp_model) {
+									TRACE_DEBUG(FULL, "Unknown attribute (vendor 0x%x, code 0x%x) ignored.", req.avp_vendor, req.avp_code);
+								} else {
+									CHECK_FCT( fd_msg_avp_new ( avp_model, 0, &avp ) );
+									value.os.len = vtlv->vendor_length - 2;
+									value.os.data = (unsigned char *)(vtlv + 1);
+									CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+									CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+								}
+								c += vtlv->vendor_length;
+								left -= vtlv->vendor_length;
+								vtlv = (struct radius_attr_vendor *)c;
+							}
+						}
+						break;
+						
+						/* Other vendors we KNOw how to convert the attributes would be added here... */
+						/* case RADIUS_VENDOR_ID_CISCO :
+							break; */
+						/* case RADIUS_VENDOR_ID_IETF : (extended RADIUS attributes)
+							break; */
+						
+						/* When we don't know, just discard the attribute... VSA are optional with regards to RADIUS anyway */
+						default:
+							/* do nothing */
+							TRACE_DEBUG(FULL, "VSA attribute from vendor %d discarded", vendor_id);
+							
+					}
+				}
+				break;
+				
+			/* Session-Timeout never present in an Access-Request */
+			/* Idle-Timeout never present in an Access-Request */
+			/* Termination-Action never present in an Access-Request */
+				
+			case RADIUS_ATTR_CALLED_STATION_ID:
+				CONV2DIAM_STR( Called_Station_Id );
+				break;
+			
+			case RADIUS_ATTR_CALLING_STATION_ID:
+				CONV2DIAM_STR( Calling_Station_Id );
+				break;
+			
+			case RADIUS_ATTR_NAS_IDENTIFIER:
+				CONV2DIAM_STR( NAS_Identifier );
+				break;
+			
+			/* Proxy-State is handled by echo_drop.rgwx plugin, we ignore it here */
+			
+			case RADIUS_ATTR_LOGIN_LAT_SERVICE:
+				CONV2DIAM_STR( Login_LAT_Service );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_NODE:
+				CONV2DIAM_STR( Login_LAT_Node );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_GROUP:
+				CONV2DIAM_STR( Login_LAT_Group );
+				break;
+			
+			/* Framed-AppleTalk-Link never present in an Access-Request */
+			/* Framed-AppleTalk-Network never present in an Access-Request */
+			/* Framed-AppleTalk-Zone never present in an Access-Request */
+			
+			case RADIUS_ATTR_CHAP_CHALLENGE:
+				CONV2DIAM_STR( CHAP_Challenge );
+				break;
+			
+			case RADIUS_ATTR_NAS_PORT_TYPE:
+				CONV2DIAM_32B( NAS_Port_Type );
+				break;
+			
+			case RADIUS_ATTR_PORT_LIMIT:
+				CONV2DIAM_32B( Port_Limit );
+				break;
+			
+			case RADIUS_ATTR_LOGIN_LAT_PORT:
+				CONV2DIAM_STR( Login_LAT_Port );
+				break;
+			
+			
+		/* RFC 3162 */	
+			case RADIUS_ATTR_NAS_IPV6_ADDRESS:
+				CONV2DIAM_STR( NAS_IPv6_Address );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_INTERFACE_ID:
+				CONV2DIAM_64B( Framed_Interface_Id );
+				break;
+				
+			case RADIUS_ATTR_FRAMED_IPV6_PREFIX:
+				CONV2DIAM_STR( Framed_IPv6_Prefix );
+				break;
+				
+			case RADIUS_ATTR_LOGIN_IPV6_HOST:
+				CONV2DIAM_STR( Login_IPv6_Host );
+				break;
+				
+			/* Framed-IPv6-Route never present in an Access-Request */
+			/* Framed-IPv6-Pool never present in an Access-Request */
+
+
+		/* RFC 2868 */
+			/* Prepare the top-level Tunneling AVP for each tag values, as needed, and add to the Diameter message.
+				This macro is called when an AVP is added inside the group, so we will not have empty grouped AVPs */
+			#define AVP_TUN_PREPARE() {										\
+						if (avp_tun == NULL) {								\
+							CHECK_MALLOC( avp_tun = calloc(sizeof(struct avp *), 32 ) );		\
+						}										\
+						tag = *(uint8_t *)(attr + 1);							\
+						if (tag > 0x1F) tag = 0;							\
+						if (avp_tun[tag] == NULL) {							\
+							CHECK_FCT( fd_msg_avp_new ( cs->dict.Tunneling, 0, &avp_tun[tag] ) );	\
+							CHECK_FCT( fd_msg_avp_add (*diam_fw, MSG_BRW_LAST_CHILD, avp_tun[tag]));\
+						}										\
+					}
+			
+			/* Convert an attribute to an OctetString AVP and add inside the Tunneling AVP corresponding to the tag */
+			#define CONV2DIAM_TUN_STR( _dictobj_ ) {						\
+				uint8_t tag;									\
+				CHECK_PARAMS( attr->length >= 3);						\
+				AVP_TUN_PREPARE();								\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				value.os.len = attr->length - (tag ? 3 : 2);					\
+				value.os.data = ((unsigned char *)(attr + 1)) + (tag ? 1 : 0);			\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) );		\
+				}
+				
+			/* Convert an attribute to a scalar AVP and add inside the Tunneling AVP corresponding to the tag */
+			#define CONV2DIAM_TUN_24B( _dictobj_ ) {						\
+				uint8_t tag;									\
+				CHECK_PARAMS( attr->length == 6);						\
+				AVP_TUN_PREPARE();								\
+				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+				{										\
+					uint8_t * v = (uint8_t *)(attr + 1);					\
+					value.u32 = (v[1] << 16) | (v[2] <<8) | v[3] ;				\
+				}										\
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+				CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) );		\
+				}
+
+			/*
+			      -  If the RADIUS message contains Tunnel information [RADTunnels],
+        			 the attributes or tagged groups should each be converted to a
+        			 Diameter Tunneling Grouped AVP set.  If the tunnel information
+        			 contains a Tunnel-Password attribute, the RADIUS encryption
+        			 must be resolved, and the password forwarded, by using Diameter
+        			 security methods.
+				    -> If the RADIUS message does not use properly the Tag info, result is unpredictable here.. 
+			*/
+			case RADIUS_ATTR_TUNNEL_TYPE:
+				CONV2DIAM_TUN_24B( Tunnel_Type );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
+				CONV2DIAM_TUN_24B( Tunnel_Medium_Type );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT:
+				CONV2DIAM_TUN_STR( Tunnel_Client_Endpoint );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT:
+				CONV2DIAM_TUN_STR( Tunnel_Server_Endpoint );
+				break;
+			
+			/* Tunnel-Password never present in an Access-Request */
+
+			case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Private_Group_Id );
+				break;
+			
+			/* Tunnel-Assignment-ID never present in an Access-Request */
+			
+			case RADIUS_ATTR_TUNNEL_PREFERENCE:
+				CONV2DIAM_TUN_24B( Tunnel_Preference );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Client_Auth_Id );
+				break;
+			
+			case RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID:
+				CONV2DIAM_TUN_STR( Tunnel_Server_Auth_Id );
+				break;
+			
+			
+		/* RFC 2869 */	
+			case RADIUS_ATTR_ARAP_PASSWORD:
+				CONV2DIAM_STR( ARAP_Password );
+				break;
+				
+			/* ARAP-Features never present in an Access-Request */
+			/* ARAP-Zone-Access never present in an Access-Request */
+			
+			case RADIUS_ATTR_ARAP_SECURITY:
+				CONV2DIAM_32B( ARAP_Security );
+				break;
+			
+			case RADIUS_ATTR_ARAP_SECURITY_DATA:
+				CONV2DIAM_STR( ARAP_Security_Data );
+				break;
+			
+			/* Password-Retry never present in an Access-Request */
+			/* Prompt never present in an Access-Request */
+			
+			case RADIUS_ATTR_CONNECT_INFO:
+				CONV2DIAM_STR( Connect_Info );
+				break;
+			
+			/* Configuration-Token never present in an Access-Request */
+			/* ARAP-Challenge-Response never present in an Access-Request */
+			/* Acct-Interim-Interval never present in an Access-Request */
+			
+			case RADIUS_ATTR_NAS_PORT_ID:
+				CONV2DIAM_STR( NAS_Port_Id );
+				break;
+			
+			/* Framed-Pool never present in an Access-Request */
+			
+				
+		/* RFC 2869 / 3579 */	
+			case RADIUS_ATTR_ORIGINATING_LINE_INFO:
+				CONV2DIAM_STR( Originating_Line_Info );
+				break;
+				
+			case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
+			case RADIUS_ATTR_EAP_MESSAGE:
+				/* It was already handled, just remove the attribute */
+				break;
+				
+		/* Default */		
+			default: /* unknown attribute */
+				/* We just keep the attribute in the RADIUS message */
+				rad_req->attr_pos[nattr_used++] = rad_req->attr_pos[idx];
+		}
+	}
+	
+	/* Destroy tunnel pointers (if we used it) */
+	free(avp_tun);
+	
+	/* Update the radius message to remove all handled attributes */
+	rad_req->attr_used = nattr_used;
+
+	/* Store the request identifier in the session (if provided) */
+	{
+		struct sess_state  *st;
+		CHECK_MALLOC(st = malloc(sizeof(struct sess_state)));
+		memcpy(st->req_auth, &rad_req->hdr->authenticator[0], 16);
+		
+		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, sess, &st ) );
+	}
+	
+	return 0;
+}
+
+static int auth_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+{
+	struct msg_hdr * hdr;
+	struct avp *avp, *next, *avp_x, *avp_y, *aoh;
+	struct avp_hdr *ahdr, *oh;
+	uint8_t buf[254]; /* to store some attributes values (with final '\0') */
+	size_t sz;
+	int ta_set = 0;
+	int no_str = 0; /* indicate if an STR is required for this server */
+	uint8_t	tuntag = 0;
+	struct sess_state  *st;
+	int error_cause = 0;
+	struct session * sess;
+	os0_t sid = NULL;
+	size_t sidlen;
+	
+	TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
+	CHECK_PARAMS(cs && diam_ans && *diam_ans && rad_fw && *rad_fw);
+	
+	/* Retrieve the request identified which was stored in the session */
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
+	if (sess) {
+		CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, sess, &st ) );
+		CHECK_FCT( fd_sess_getsid(sess, &sid, &sidlen) );
+	} /* else ? */
+	
+	/*	
+	      -  If the Diameter Command-Code is set to AA-Answer and the
+        	 Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH, the
+        	 gateway must send a RADIUS Access-Challenge.  This must have
+        	 the Origin-Host, Origin-Realm, and Diameter Session-Id AVPs
+        	 encapsulated in the RADIUS State attribute, with the prefix
+        	 "Diameter/", concatenated in the above order separated with "/"
+        	 characters, in UTF-8 [UTF-8].  This is necessary to ensure that
+        	 the Translation Agent receiving the subsequent RADIUS Access-
+        	 Request will have access to the Session Identifier and be able
+        	 to set the Destination-Host to the correct value.
+		 	-> done here below
+		 
+	      -  If the Command-Code is set to AA-Answer, the Diameter Session-
+        	 Id AVP is saved in a new RADIUS Class attribute whose format
+        	 consists of the string "Diameter/" followed by the Diameter
+        	 Session Identifier.  This will ensure that the subsequent
+        	 Accounting messages, which could be received by any Translation
+        	 Agent, would have access to the original Diameter Session
+        	 Identifier.
+		 	-> done here but only for Access-Accept messages (Result-Code = success)
+	 */
+	
+	/* MACROS to help in the process: convert AVP data to RADIUS attributes. */
+	/* Control large attributes:  _trunc_ = 0 => error; _trunc_ = 1 => truncate; _trunc = 2 => create several attributes */
+	#define CONV2RAD_STR( _attr_, _data_, _len_, _trunc_)	{					\
+		size_t __l = (size_t)(_len_);								\
+		size_t __off = 0;									\
+		TRACE_DEBUG(FULL, "Converting AVP to "#_attr_);						\
+		if ((_trunc_) == 0) {									\
+			CHECK_PARAMS( __l <= 253 );							\
+		}											\
+		if ((__l > 253) && (_trunc_ == 1)) {							\
+			TRACE_DEBUG(INFO, "[auth.rgwx] AVP truncated in "#_attr_);			\
+			__l = 253;									\
+		}											\
+		do {											\
+			size_t __w = (__l > 253) ? 253 : __l;						\
+			CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (_data_) + __off, __w));	\
+			__off += __w;									\
+			__l   -= __w;									\
+		} while (__l);										\
+	}
+
+	#define CONV2RAD_32B( _attr_, _data_)	{							\
+		uint32_t __v = htonl((uint32_t)(_data_));						\
+		TRACE_DEBUG(FULL, "Converting AVP to "#_attr_);						\
+		CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+	}
+
+	#define CONV2RAD_64B( _attr_, _data_)	{							\
+		uint64_t __v = htonll((uint64_t)(_data_));						\
+		TRACE_DEBUG(FULL, "Converting AVP to "#_attr_);						\
+		CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+	}
+
+	/* Search the different AVPs we handle here */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Host, &aoh) );
+	CHECK_FCT( fd_msg_avp_hdr ( aoh, &oh ) );
+
+	/* Check the Diameter error code */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Result_Code, &avp) );
+	ASSERT( avp ); /* otherwise the message should have been discarded a lot earlier because of ABNF */
+	CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+	switch (ahdr->avp_value->u32) {
+		case ER_DIAMETER_MULTI_ROUND_AUTH:
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_CHALLENGE;
+			break;
+		case ER_DIAMETER_SUCCESS:
+		case ER_DIAMETER_LIMITED_SUCCESS:
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_ACCEPT;
+			break;
+		
+		default:
+			/* Can we convert the value to a natural Error-Cause ? */
+			switch (ahdr->avp_value->u32) {
+				case ER_DIAMETER_AVP_UNSUPPORTED:
+					error_cause = 401; /* Unsupported Attribute */
+					break;
+					
+				case ER_DIAMETER_MISSING_AVP:
+					error_cause = 402; /* Missing Attribute */
+					break;
+					
+				case ER_DIAMETER_UNABLE_TO_COMPLY:
+					error_cause = 404; /* Invalid Request */
+					break;
+					
+				case ER_DIAMETER_APPLICATION_UNSUPPORTED:
+					error_cause = 405; /* Unsupported Service */
+					break;
+					
+				case ER_DIAMETER_COMMAND_UNSUPPORTED:
+					error_cause = 406; /* Unsupported Extension */
+					break;
+					
+				case ER_DIAMETER_INVALID_AVP_VALUE:
+					error_cause = 407; /* Invalid Attribute Value */
+					break;
+					
+				case ER_DIAMETER_AVP_NOT_ALLOWED:
+					error_cause = 501; /* Administratively Prohibited */
+					break;
+					
+				case ER_DIAMETER_REALM_NOT_SERVED:
+				case ER_DIAMETER_LOOP_DETECTED:
+				case ER_DIAMETER_UNKNOWN_PEER:
+				case ER_DIAMETER_UNABLE_TO_DELIVER:
+					error_cause = 502; /* Request Not Routable (Proxy) */
+					break;
+					
+				case ER_DIAMETER_UNKNOWN_SESSION_ID:
+					error_cause = 503; /* Session Context Not Found */
+					break;
+					
+				case ER_DIAMETER_TOO_BUSY:
+				case ER_DIAMETER_OUT_OF_SPACE:
+					error_cause = 506; /* Resources Unavailable */
+					break;
+					
+#if 0
+			/* remaining Diameter Result-Code & RADIUS Error-Cause */
+				case ER_DIAMETER_REDIRECT_INDICATION:
+				case ER_DIAMETER_INVALID_HDR_BITS:
+				case ER_DIAMETER_INVALID_AVP_BITS:
+				case ER_DIAMETER_AUTHENTICATION_REJECTED:
+				case ER_ELECTION_LOST:
+				case ER_DIAMETER_AUTHORIZATION_REJECTED:
+				case ER_DIAMETER_RESOURCES_EXCEEDED:
+				case ER_DIAMETER_CONTRADICTING_AVPS:
+				case ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES
+				case ER_DIAMETER_NO_COMMON_APPLICATION:
+				case ER_DIAMETER_UNSUPPORTED_VERSION:
+				case ER_DIAMETER_INVALID_BIT_IN_HEADER:
+				case ER_DIAMETER_INVALID_AVP_LENGTH:
+				case ER_DIAMETER_INVALID_MESSAGE_LENGTH:
+				case ER_DIAMETER_INVALID_AVP_BIT_COMBO:
+				case ER_DIAMETER_NO_COMMON_SECURITY:
+					error_cause = 403; /* NAS Identification Mismatch */
+					error_cause = 504; /* Session Context Not Removable */
+					error_cause = 505; /* Other Proxy Processing Error */
+					error_cause = 507; /* Request Initiated */
+					error_cause = 508; /* Multiple Session Selection Unsupported */
+#endif /* 0 */
+			}
+			/* In any case, the following is processed: */
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_REJECT;
+			fd_log_debug("[auth.rgwx] Received Diameter answer with error code '%d' from server '%.*s', session %.*s, translating into Access-Reject",
+					ahdr->avp_value->u32, 
+					(int)oh->avp_value->os.len, oh->avp_value->os.data,
+					(int)sidlen, sid);
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Message, &avp_x) );
+			if (avp_x) {
+				CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
+				fd_log_debug("[auth.rgwx]   Error-Message content: '%.*s'",
+						(int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
+			}
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Reporting_Host, &avp_x) );
+			if (avp_x) {
+				CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
+				fd_log_debug("[auth.rgwx]   Error-Reporting-Host: '%.*s'",
+						(int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
+			}
+			CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Failed_AVP, &avp_x) );
+			if (avp_x) {
+				fd_log_debug("[auth.rgwx]   Failed-AVP was included in the message.");
+				/* Dump its content ? */
+			}
+	}
+	/* Remove this Result-Code avp */
+	CHECK_FCT( fd_msg_free( avp ) );
+	
+	/* Creation of the State or Class attribute with session information */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Realm, &avp) );
+	CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+	
+	/* Now, save the session-id and eventually server info in a STATE or CLASS attribute */
+	if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_CHALLENGE) {
+		if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), "Diameter/%.*s/%.*s/%.*s", 
+				(int)oh->avp_value->os.len,  (char *)oh->avp_value->os.data,
+				(int)ahdr->avp_value->os.len,  (char *)ahdr->avp_value->os.data,
+				(int)sidlen, (char *)sid))) {
+			TRACE_DEBUG(INFO, "Data truncated in State attribute: %s", buf);
+		}
+		CONV2RAD_STR(RADIUS_ATTR_STATE, buf, sz, 0);
+	}
+
+	if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+		/* Add the Session-Id */
+		if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), "Diameter/%.*s", 
+				(int)sidlen, sid))) {
+			TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf);
+		}
+		CONV2RAD_STR(RADIUS_ATTR_CLASS, buf, sz, 0);
+	}
+	
+	/* Unlink the Origin-Realm now; the others are unlinked at the end of this function */
+	CHECK_FCT( fd_msg_free( avp ) );
+	
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Session_Timeout, &avp) );
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Authorization_Lifetime, &avp_x) );
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Re_Auth_Request_Type, &avp_y) );
+	/*	
+	   When translating a Diameter AA-Answer (with successful result code)
+	   to RADIUS Access-Accept that contains a Session-Timeout or
+	   Authorization-Lifetime AVP, take the following steps:
+	   
+	      -  If the Diameter message contains a Session-Timeout AVP but no
+        	 Authorization-Lifetime AVP, translate it to a Session-Timeout
+        	 attribute (not a Termination-Action).
+	*/
+	if ((avp != NULL) && (avp_x == NULL)) {
+		CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+		CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
+	}
+	
+	/*	
+	      -  If the Diameter message contains an Authorization-Lifetime AVP
+        	 but no Session-Timeout AVP, translate it to a Session-Timeout
+        	 attribute and a Termination-Action set to AA-REQUEST.  (Remove
+        	 Authorization-Lifetime and Re-Auth-Request-Type.)
+	*/
+	if ((avp == NULL) && (avp_x != NULL)) {
+		CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
+		CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
+		CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
+		ta_set = 1;
+	}
+	
+	/*	
+	      -  If the Diameter message has both, the Session-Timeout must be
+        	 greater than or equal to the Authorization-Lifetime (required
+        	 by [BASE]).  Translate it to a Session-Timeout value (with
+        	 value from Authorization-Lifetime AVP, the smaller one) and
+        	 with the Termination-Action set to AA-REQUEST.  (Remove the
+        	 Authorization-Lifetime and Re-Auth-Request-Type.)
+	*/
+	if ((avp != NULL) && (avp_x != NULL)) {
+		CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
+		CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
+		CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
+		ta_set = 1;
+	}
+	
+	/*  -> Not too sure about Auth-Grace-Period... we'll just discard it for now */
+	
+	if (avp) {
+		CHECK_FCT( fd_msg_free( avp ) );
+	}
+	if (avp_x) {
+		CHECK_FCT( fd_msg_free( avp_x ) );
+	}
+	if (avp_y) {
+		CHECK_FCT( fd_msg_free( avp_y ) );
+	}
+	
+	
+	/*
+	      -  If a Proxy-State attribute was present in the RADIUS request,
+        	 the same attribute is added in the response.  This information
+        	 may be found in the Proxy-Info AVP group, or in a local state
+        	 table.
+		 	-> handled by sub_echo_drop
+
+	      -  If state information regarding the RADIUS request was saved in
+        	 a Proxy-Info AVP or local state table, the RADIUS Identifier
+        	 and UDP IP Address and port number are extracted and used in
+        	 issuing the RADIUS reply.
+		 	-> was saved with the full request
+	*/
+	
+	
+	/* Now loop in the list of AVPs and convert those that we know how */
+	CHECK_FCT( fd_msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) );
+	
+	while (next) {
+		int handled = 1;
+		avp = next;
+		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL) );
+		
+		CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+		
+		if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+			switch (ahdr->avp_code) {
+		/* In case of Diameter error, include the Reply-Message attribute */
+				case DIAM_ATTR_ERROR_MESSAGE:
+					CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_ERROR_REPORTING_HOST:
+					{
+						char buf[254];
+						int bsz = snprintf(buf, sizeof(buf), "Error-Reporting-Host: %*s", (int)(ahdr->avp_value->os.len), ahdr->avp_value->os.data);
+						CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, (uint8_t *)buf, bsz, 2);
+					}
+					break;
+				
+				case DIAM_ATTR_FAILED_AVP:
+					{
+						struct avp * favp;
+						CHECK_FCT( fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &favp, NULL) );
+						if (favp) {
+							char buf[254];
+							int bsz;
+							struct dict_object * favp_model;
+							
+							CHECK_FCT( fd_msg_model(favp, &favp_model) );
+							if (favp_model) {
+								struct dict_avp_data fadata;
+								CHECK_FCT( fd_dict_getval(favp_model, &fadata) );
+								bsz = snprintf(buf, sizeof(buf), "Failed-AVP: %s", fadata.avp_name);
+							} else {
+								struct avp_hdr * favp_hdr;
+								CHECK_FCT( fd_msg_avp_hdr ( favp, &favp_hdr ) );
+								bsz = snprintf(buf, sizeof(buf), "Failed-AVP: code %u, vendor %u", favp_hdr->avp_code, favp_hdr->avp_vendor);
+							}
+							CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, (uint8_t *)buf, bsz, 2);
+						}
+					}
+					break;
+					
+		/* RFC 4005 (AVP in the order of the AA-Request/Answer AVP Table) */
+				case DIAM_ATTR_ACCT_INTERIM_INTERVAL:
+					CONV2RAD_32B(RADIUS_ATTR_ACCT_INTERIM_INTERVAL, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_ARAP_CHALLENGE_RESPONSE:
+					CONV2RAD_STR(RADIUS_ATTR_ARAP_CHALLENGE_RESPONSE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_ARAP_FEATURES:
+					CONV2RAD_STR(RADIUS_ATTR_ARAP_FEATURES, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				/* ARAP-Password is not present in answers */
+					
+				case DIAM_ATTR_ARAP_SECURITY:
+					CONV2RAD_32B(RADIUS_ATTR_ARAP_SECURITY, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_ARAP_SECURITY_DATA:
+					CONV2RAD_STR(RADIUS_ATTR_ARAP_SECURITY_DATA, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_ARAP_ZONE_ACCESS:
+					CONV2RAD_32B(RADIUS_ATTR_ARAP_ZONE_ACCESS, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_AUTH_APPLICATION_ID:
+					/* We just remove this AVP */
+					break;
+					
+				case DIAM_ATTR_AUTH_GRACE_PERIOD:
+					/* We just remove this AVP (?) */
+					break;
+				
+				case DIAM_ATTR_AUTH_REQUEST_TYPE:
+					/* We only check the value */
+					if (ahdr->avp_value->u32 != 3) {
+						fd_log_debug("[auth.rgwx] Received Diameter answer with Auth-Request-Type set to %d (%s) from server %.*s, session %.*s."
+								"  This may cause interoperability problems with RADIUS.",
+								ahdr->avp_value->u32,
+								(ahdr->avp_value->u32 == 1) ? "AUTHENTICATE_ONLY" :
+									((ahdr->avp_value->u32 == 2) ? "AUTHORIZE_ONLY" : "???"),
+								(int)oh->avp_value->os.len, oh->avp_value->os.data, 
+								(int)sidlen, sid);
+					}
+					break;
+				
+				case DIAM_ATTR_AUTH_SESSION_STATE:
+					if ((!ta_set) && (ahdr->avp_value->u32 == ACV_ASS_STATE_MAINTAINED)) {
+						CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
+					}
+					
+					if (ahdr->avp_value->u32 == ACV_ASS_NO_STATE_MAINTAINED) {
+						no_str = 1;
+					}
+					break;
+					
+				/* Authorization-Lifetime already handled */
+				
+				case DIAM_ATTR_CALLBACK_ID:
+					CONV2RAD_STR(RADIUS_ATTR_CALLBACK_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				
+				case DIAM_ATTR_CALLBACK_NUMBER:
+					CONV2RAD_STR(RADIUS_ATTR_CALLBACK_NUMBER, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				
+				/* Called-Station-Id is not present in answers */
+				/* Calling-Station-Id is not present in answers */
+				/* CHAP-Auth is not present in answers */
+				/* CHAP-Challenge is not present in answers */
+					
+				case DIAM_ATTR_CLASS:
+					CONV2RAD_STR(RADIUS_ATTR_CLASS, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+				
+				case DIAM_ATTR_CONFIGURATION_TOKEN:
+					/* We might as well remove it since it's not supposed to be sent to the NAS... */
+					CONV2RAD_STR(RADIUS_ATTR_CONFIGURATION_TOKEN, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+				
+				/* Connect-Info is not present in answers */
+				
+				case DIAM_ATTR_FILTER_ID:
+					CONV2RAD_STR(RADIUS_ATTR_FILTER_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_LINK:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_APPLETALK_LINK, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_NETWORK:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_APPLETALK_NETWORK, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_APPLETALK_ZONE:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_APPLETALK_ZONE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_COMPRESSION:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_COMPRESSION,  ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_INTERFACE_ID:
+					CONV2RAD_64B(RADIUS_ATTR_FRAMED_INTERFACE_ID,  ahdr->avp_value->u64);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IP_ADDRESS:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IP_ADDRESS,  ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IP_NETMASK:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IP_NETMASK, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_PREFIX:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_PREFIX, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_POOL:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_POOL, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPV6_ROUTE:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_ROUTE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_IPX_NETWORK:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_IPX_NETWORK, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_MTU:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_MTU, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_POOL:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_POOL, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_PROTOCOL:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_PROTOCOL, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_FRAMED_ROUTE:
+					CONV2RAD_STR(RADIUS_ATTR_FRAMED_ROUTE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_FRAMED_ROUTING:
+					CONV2RAD_32B(RADIUS_ATTR_FRAMED_ROUTING, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_IDLE_TIMEOUT:
+					CONV2RAD_32B(RADIUS_ATTR_IDLE_TIMEOUT, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_LOGIN_IP_HOST:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_IP_HOST, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_LOGIN_IPV6_HOST:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_IPV6_HOST, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_GROUP:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_GROUP, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_NODE:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_NODE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_PORT:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_PORT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_LAT_SERVICE:
+					CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_SERVICE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+					
+				case DIAM_ATTR_LOGIN_SERVICE:
+					CONV2RAD_32B(RADIUS_ATTR_LOGIN_SERVICE, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_LOGIN_TCP_PORT:
+					CONV2RAD_32B(RADIUS_ATTR_LOGIN_TCP_PORT, ahdr->avp_value->u32);
+					break;
+					
+			/*
+			      -							        If the
+        			 Multi-Round-Time-Out AVP is present, the value of the AVP MUST
+        			 be inserted in the RADIUS Session-Timeout AVP.
+
+			      o  As described in [NASREQ], if the Result-Code AVP set to
+				 DIAMETER_MULTI_ROUND_AUTH and the Multi-Round-Time-Out AVP is
+				 present, it is translated to the RADIUS Session-Timeout attribute.
+			*/
+				case DIAM_ATTR_MULTI_ROUND_TIMEOUT:
+					CONV2RAD_32B(RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_NAS_FILTER_RULE:
+					/* This is not translatable to RADIUS */
+					fd_log_debug("[auth.rgwx] Received Diameter answer with non-translatable NAS-Filter-Rule AVP from '%.*s' (session: '%.*s'), ignoring.",
+							(int)oh->avp_value->os.len, oh->avp_value->os.data,
+							(int)sidlen, sid);
+					handled = 0;
+					break;
+					
+				/* NAS-Identifier is not present in answers */
+				/* NAS-IP-Address is not present in answers */
+				/* NAS-IPv6-Address is not present in answers */
+				/* NAS-Port is not present in answers */
+				/* NAS-Port-Id is not present in answers */
+				/* NAS-Port-Type is not present in answers */
+				
+				case DIAM_ATTR_ORIGIN_AAA_PROTOCOL:
+					/* We just remove this AVP */
+					break;
+					
+				/* Originating-Line-Info is not present in answers */
+				
+				case DIAM_ATTR_PASSWORD_RETRY:
+					CONV2RAD_32B(RADIUS_ATTR_PASSWORD_RETRY, ahdr->avp_value->u32);
+					break;
+				
+				case DIAM_ATTR_PORT_LIMIT:
+					CONV2RAD_32B(RADIUS_ATTR_PORT_LIMIT, ahdr->avp_value->u32);
+					break;
+				
+				case DIAM_ATTR_PROMPT:
+					CONV2RAD_32B(RADIUS_ATTR_PROMPT, ahdr->avp_value->u32);
+					break;
+					
+				case DIAM_ATTR_QOS_FILTER_RULE:
+					/* This is not translatable to RADIUS */
+					fd_log_debug("[auth.rgwx] Received Diameter answer with non-translatable QoS-Filter-Rule AVP from '%.*s' (session: '%.*s'), ignoring.",
+							(int)oh->avp_value->os.len, oh->avp_value->os.data,
+							(int)sidlen, sid);
+					handled = 0;
+					break;
+					
+				/* Re-Auth-Request-Type already handled */
+				
+				case DIAM_ATTR_REPLY_MESSAGE:
+					CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_SERVICE_TYPE:
+					CONV2RAD_32B(RADIUS_ATTR_SERVICE_TYPE, ahdr->avp_value->u32);
+					break;
+				
+				case DIAM_ATTR_STATE:
+					CONV2RAD_STR(RADIUS_ATTR_STATE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
+					break;
+					
+				case DIAM_ATTR_TUNNELING:
+					{
+#define CONV2RAD_TUN_STR( _attr_, _data_, _len_, _trunc_)	{				\
+	size_t __l = (size_t)(_len_);								\
+	size_t __w = (__l > 252) ? 252 : __l;							\
+	size_t __off = 0;									\
+	if ((_trunc_) == 0) {									\
+		CHECK_PARAMS( __l <= 252 );							\
+	}											\
+	if ((__l > 252) && (_trunc_ == 1)) {							\
+		TRACE_DEBUG(FULL, "Attribute truncated!");					\
+		__l = 252;									\
+	}											\
+	buf[0] = tuntag;									\
+	memcpy(&buf[1], (_data_), __w);								\
+	CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), &buf[0], __w + 1));			\
+	while (__l -= __w) {									\
+		__off += __w;									\
+		__w = (__l > 253) ? 253 : __l;							\
+		CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (_data_) + __off, __w));	\
+	}											\
+}
+
+#define CONV2RAD_TUN_32B( _attr_, _data_)	{						\
+	uint32_t __v = htonl((uint32_t)(_data_) | (tuntag << 24));				\
+	CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+}
+						struct avp *inavp, *innext;
+						tuntag++;
+						CHECK_FCT( fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &innext, NULL) );
+						while (innext) {
+							inavp = innext;
+							CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &innext, NULL) );
+							CHECK_FCT( fd_msg_avp_hdr ( inavp, &ahdr ) );
+							
+							if ( ! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+								switch (ahdr->avp_code) {
+									case DIAM_ATTR_TUNNEL_TYPE:
+										CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_TYPE, ahdr->avp_value->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_MEDIUM_TYPE:
+										CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, ahdr->avp_value->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_CLIENT_ENDPOINT:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_SERVER_ENDPOINT:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_PREFERENCE:
+										CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_PREFERENCE, ahdr->avp_value->u32);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_CLIENT_AUTH_ID:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_SERVER_AUTH_ID:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_ASSIGNMENT_ID:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_ASSIGNMENT_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+										
+									case DIAM_ATTR_TUNNEL_PASSWORD:
+										{
+											/* This AVP must be encoded for RADIUS (similar to radius_msg_add_attr_user_password)
+											    0                   1                   2                   3
+											    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+											   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+											   |     Type      |    Length     |     Tag       |   Salt
+											   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+											      Salt (cont)  |   String ...
+											   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+											*/
+											size_t pos;
+											int i;
+											uint8_t * secret;	/* S */
+											size_t secret_len;
+											uint8_t hash[16];	/* b(i) */
+											const uint8_t *addr[3];
+											size_t len[3];
+											
+											/* We need the request authenticator */
+											CHECK_PARAMS(st);
+
+											/* Retrieve the shared secret */
+											CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
+											
+											/* Beginning of the buffer */
+											buf[0] = tuntag;
+											buf[1] = (uint8_t)(lrand48()); /* A (hi bits) */
+											buf[2] = (uint8_t)(lrand48()); /* A (low bits) */
+											
+											/* The plain text string P */
+											CHECK_PARAMS(ahdr->avp_value->os.len < 240);
+											buf[3] = ahdr->avp_value->os.len;
+											memcpy(&buf[4], ahdr->avp_value->os.data, ahdr->avp_value->os.len);
+											memset(&buf[4 + ahdr->avp_value->os.len], 0, sizeof(buf) - 4 - ahdr->avp_value->os.len);
+											
+											/* Initial b1 = MD5(S + R + A) */
+											addr[0] = secret;
+											len[0] = secret_len;
+											addr[1] = st->req_auth;
+											len[1] = 16;
+											addr[2] = &buf[1];
+											len[2] = 2;
+											md5_vector(3, addr, len, hash);
+											
+											/* Initial c(1) = p(1) xor b(1) */
+											for (i = 0; i < 16; i++) {
+												buf[i + 3] ^= hash[i];
+											}
+											pos = 16;
+											
+											/* loop */
+											while (pos < ahdr->avp_value->os.len + 1) {
+												addr[0] = secret;
+												len[0] = secret_len;
+												addr[1] = &buf[pos - 13];
+												len[1] = 16;
+												/* b(i) = MD5( S + c(i-1) */
+												md5_vector(2, addr, len, hash);
+												
+												/* c(i) = p(i) xor b(i) */
+												for (i = 0; i < 16; i++)
+													buf[pos + i + 3] ^= hash[i];
+
+												pos += 16;
+											}
+											
+											CONV2RAD_STR(RADIUS_ATTR_TUNNEL_PASSWORD, &buf[0], pos + 3, 0);
+										}
+										break;
+										
+									case DIAM_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+										CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+										break;
+									
+									default:
+										TRACE_DEBUG(FULL, "Ignored unknown AVP inside Tunneling AVP (%d)", ahdr->avp_code);
+								}
+							} else {
+								TRACE_DEBUG(FULL, "Ignored unknown Vendor AVP inside Tunneling AVP (%d, %d)", ahdr->avp_vendor, ahdr->avp_code);
+							}
+						}
+					}
+					break;
+					
+				case DIAM_ATTR_USER_NAME:
+					CONV2RAD_STR(RADIUS_ATTR_USER_NAME, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				
+				/* User-Password never present in answers */
+					
+		/* RFC 4072 (AVP in the order of the EAP Command AVP Table) */
+			/*
+			      o  Diameter Accounting-EAP-Auth-Method AVPs, if present, are
+				 discarded.
+			*/
+				case DIAM_ATTR_ACCOUNTING_EAP_AUTH_METHOD:
+					break;
+					
+			/*
+			      o  Diameter EAP-Master-Session-Key AVP can be translated to the
+				 vendor-specific RADIUS MS-MPPE-Recv-Key and MS-MPPE-Send-Key
+				 attributes [RFC2548].  The first up to 32 octets of the key is
+				 stored into MS-MPPE-Recv-Key, and the next up to 32 octets (if
+				 present) are stored into MS-MPPE-Send-Key.  The encryption of this
+				 attribute is described in [RFC2548].
+			*/
+				case DIAM_ATTR_EAP_MASTER_SESSION_KEY:
+					{
+						uint8_t * secret;	/* S */
+						size_t secret_len;
+						size_t recv_len, send_len;
+
+						/* We need the request authenticator */
+						CHECK_PARAMS(st);
+
+						/* Retrieve the shared secret */
+						CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
+						
+						if (ahdr->avp_value->os.len != 64) {
+							TRACE_DEBUG(INFO, "Received EAP-Master-Session-Key attribute with length %zd != 64.", ahdr->avp_value->os.len)
+						}
+						
+						CHECK_PARAMS(ahdr->avp_value->os.len <= 64);
+						recv_len = ahdr->avp_value->os.len >= 32 ? 32 : ahdr->avp_value->os.len;
+						send_len = ahdr->avp_value->os.len - recv_len;
+						
+						if ( ! radius_msg_add_mppe_keys(*rad_fw, st->req_auth, secret, secret_len, 
+								ahdr->avp_value->os.data + recv_len, send_len,
+								ahdr->avp_value->os.data, recv_len) ) {
+							TRACE_DEBUG(INFO, "Error while converting EAP-Master-Session-Key to RADIUS message");
+							return ENOMEM;
+						}
+					}
+					break;
+				
+				case DIAM_ATTR_EAP_KEY_NAME:
+					CONV2RAD_STR(RADIUS_ATTR_EAP_KEY_NAME, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				
+			/*
+			      o  Diameter EAP-Payload AVP is translated to RADIUS EAP-Message
+				 attribute(s).  If necessary, the value is split into multiple
+				 RADIUS EAP-Message attributes.
+			*/
+				case DIAM_ATTR_EAP_PAYLOAD:
+					if ( ! radius_msg_add_eap(*rad_fw, ahdr->avp_value->os.data, ahdr->avp_value->os.len) ) {
+						TRACE_DEBUG(INFO, "Error while converting EAP payload to RADIUS message");
+						return ENOMEM;
+					}
+					break;
+					
+			/*
+			      o  Diameter EAP-Reissued-Payload AVP is translated to a message that
+				 contains RADIUS EAP-Message attribute(s), and a RADIUS Error-Cause
+				 attribute [RFC3576] with value 202 (decimal), "Invalid EAP Packet
+				 (Ignored)" [RFC3579].
+			*/
+				case DIAM_ATTR_EAP_REISSUED_PAYLOAD:
+					if ( ! radius_msg_add_eap(*rad_fw, ahdr->avp_value->os.data, ahdr->avp_value->os.len) ) {
+						TRACE_DEBUG(INFO, "Error while converting EAP reissued payload to RADIUS message");
+						return ENOMEM;
+					}
+					
+					error_cause = 202; /* Invalid EAP Packet */
+					break;
+			
+				default:
+					/* Leave the AVP in the message for further treatment */
+					handled = 0;
+			}
+		} else {
+			/* Vendor-specific AVPs */
+			switch (ahdr->avp_vendor) {
+				
+				default: /* unknown vendor */
+					handled = 0;
+			}
+		}
+		
+		if (handled) {
+			CHECK_FCT( fd_msg_free( avp ) );
+		}
+	}
+	
+	CHECK_FCT( fd_msg_free( aoh ) );
+	free(st);
+	
+	if (error_cause) {
+		if ( ! radius_msg_add_attr_int32(*rad_fw, RADIUS_ATTR_ERROR_CAUSE, error_cause) ) {
+			TRACE_DEBUG(INFO, "Error while adding Error-Cause attribute in RADIUS message");
+			return ENOMEM;
+		}
+	}
+
+	if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+		/* Add the auth-application-id required for STR, or 0 if no STR is required */
+		CHECK_FCT( fd_msg_hdr( *diam_ans, &hdr ) );
+		if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), CLASS_AAI_PREFIX "%u", 
+				no_str ? 0 : hdr->msg_appl))) {
+			TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf);
+		}
+		CONV2RAD_STR(RADIUS_ATTR_CLASS, buf, sz, 0);
+	}
+	
+	return 0;
+}
+
+/* The exported symbol */
+struct rgw_api rgwp_descriptor = {
+	.rgwp_name       = "auth",
+	.rgwp_conf_parse = auth_conf_parse,
+	.rgwp_conf_free  = auth_conf_free,
+	.rgwp_rad_req    = auth_rad_req,
+	.rgwp_diam_ans   = auth_diam_ans
+};	
diff --git a/extensions/app_radgw/rgwx_debug.c b/extensions/app_radgw/rgwx_debug.c
new file mode 100644
index 0000000..282b028
--- /dev/null
+++ b/extensions/app_radgw/rgwx_debug.c
@@ -0,0 +1,143 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Debug plugin for app_radgw */
+
+#include "rgw_common.h"
+
+/* Store the configuration string in the state */
+static int debug_conf_parse ( char * conf_file, struct rgwp_config ** state )
+{
+	TRACE_ENTRY("%p %p", conf_file, state);
+	CHECK_PARAMS(state);
+	
+	*state = (void *)conf_file;
+	
+	return 0;
+}
+
+
+/* Function to display the content of a RADIUS message (more friendly way than radius_msg_dump) */
+static void debug_dump_radius(struct radius_msg *msg)
+{
+	unsigned char *auth;
+	size_t i;
+	
+	auth =  &(msg->hdr->authenticator[0]);
+	fd_log_debug(" id  : 0x%02hhx, code: %hhd (%s)", msg->hdr->identifier, msg->hdr->code, rgw_msg_code_str(msg->hdr->code));
+	fd_log_debug(" auth: %02hhx %02hhx %02hhx %02hhx  %02hhx %02hhx %02hhx %02hhx",
+			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",
+			auth[8],  auth[9],  auth[10], auth[11], 
+			auth[12], auth[13], auth[14], auth[15]);
+	for (i = 0; i < msg->attr_used; i++) {
+		struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->buf + msg->attr_pos[i]);
+		fd_log_debug("  - len:%3hhu, type:0x%02hhx (%s)", 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... */
+	}
+}
+
+/* Function called when a new RADIUS message is being converted to Diameter */
+static int debug_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+{
+	TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
+	
+	fd_log_debug("------------- RADIUS/Diameter Request Debug%s%s%s -------------", cs ? " [" : "", cs ? (char *)cs : "", cs ? "]" : "");
+	
+	if (!rad_req) {
+		fd_log_debug(" RADIUS request: NULL pointer");
+	} else {
+		fd_log_debug(" RADIUS request (%p) DUMP:", rad_req);
+		debug_dump_radius(rad_req);
+	}
+	
+	if (!rad_ans || ! *rad_ans) {
+		fd_log_debug(" RADIUS answer: NULL pointer");
+	} else {
+		fd_log_debug(" RADIUS answer (%p) DUMP:", *rad_ans);
+		debug_dump_radius(*rad_ans);
+	}
+	
+	if (!diam_fw || ! *diam_fw) {
+		fd_log_debug(" Diameter message: NULL pointer");
+	} else {
+		char * buf = NULL; size_t buflen;
+		CHECK_MALLOC( fd_msg_dump_treeview(&buf, &buflen, NULL, *diam_fw, NULL, 0, 1) );
+		fd_log_debug(" Diameter message (%p) DUMP: %s", *diam_fw, buf);
+		free(buf);
+	}
+	
+	fd_log_debug("===========  Debug%s%s%s complete =============", cs ? " [" : "", cs ? (char *)cs : "", cs ? "]" : "");
+	
+	return 0;
+}
+
+/* This one, when Diameter answer is converted to RADIUS */
+static int debug_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+{
+	TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
+
+	fd_log_debug("------------- RADIUS/Diameter Answer Debug%s%s%s -------------", cs ? " [" : "", cs ? (char *)cs : "", cs ? "]" : "");
+	
+	if (!diam_ans || ! *diam_ans) {
+		fd_log_debug(" Diameter message: NULL pointer");
+	} else {
+		char * buf = NULL; size_t buflen;
+		CHECK_MALLOC( fd_msg_dump_treeview(&buf, &buflen, NULL, *diam_ans, NULL, 0, 1) );
+		fd_log_debug(" Diameter message (%p) DUMP: %s", *diam_ans, buf);
+		free(buf);
+	}
+	
+	if (!rad_fw || ! *rad_fw) {
+		fd_log_debug(" RADIUS answer: NULL pointer");
+	} else {
+		fd_log_debug(" RADIUS answer (%p) DUMP:", *rad_fw);
+		debug_dump_radius(*rad_fw);
+	}
+	
+	fd_log_debug("===========  Debug%s%s%s complete =============", cs ? " [" : "", cs ? (char *)cs : "", cs ? "]" : "");
+	return 0;
+}
+
+
+/* The exported symbol */
+struct rgw_api rgwp_descriptor = {
+	.rgwp_name       = "debug",
+	.rgwp_conf_parse = debug_conf_parse,
+	.rgwp_conf_free  = NULL,
+	.rgwp_rad_req    = debug_rad_req,
+	.rgwp_diam_ans   = debug_diam_ans
+};	
diff --git a/extensions/app_radgw/rgwx_echodrop.c b/extensions/app_radgw/rgwx_echodrop.c
new file mode 100644
index 0000000..c8f4333
--- /dev/null
+++ b/extensions/app_radgw/rgwx_echodrop.c
@@ -0,0 +1,309 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* See rgwx_echodrop.h for details */
+
+#include "rgwx_echodrop.h"
+
+struct sess_state {
+	struct fd_list sentinel;
+};
+
+/* If a session is destroyed, empty the list of ed_saved_attribute */
+static void state_delete(struct sess_state * arg, os0_t sid, void * opaque) {
+	while (!FD_IS_LIST_EMPTY(&arg->sentinel)) {
+		struct ed_saved_attribute * esa = (struct ed_saved_attribute *)(arg->sentinel.next);
+		fd_list_unlink(&esa->chain);
+		free(esa);
+	}
+	free(arg);
+}
+
+static DECLARE_FD_DUMP_PROTOTYPE(ed_session_state_dump, struct sess_state * st)
+{
+	struct fd_list * li;
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "[rgwx sess_state](@%p):\n", st), return NULL);	
+	for (li = st->sentinel.next; li != &st->sentinel; li = li->next) {
+		struct ed_saved_attribute * esa = (struct ed_saved_attribute *)(li);
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "[rgwx sess_state {esa}] t:%2hhx l:%2hhx d:", esa->attr.type, esa->attr.length), return NULL);
+		CHECK_MALLOC_DO( fd_dump_extend_hexdump(FD_DUMP_STD_PARAMS, (&esa->attr.length) + 1, esa->attr.length - 2, 0,0), return NULL);
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
+	}
+	return *buf;
+}
+
+/* Initialize the plugin and parse the configuration. */
+static int ed_conf_parse(char * conffile, struct rgwp_config ** state)
+{
+	struct rgwp_config * new;
+	
+	TRACE_ENTRY("%p %p", conffile, state);
+	CHECK_PARAMS( state );
+	CHECK_PARAMS_DO( conffile, { fd_log_debug("[echodrop.rgwx] The configuration file is not optional for this plugin."); return EINVAL; } );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
+	memset(new, 0, sizeof(struct rgwp_config));
+	
+	/* Initialize the list of attributes to handle */
+	fd_list_init(&new->attributes, NULL);
+	
+	/* Create the session handler */
+	CHECK_FCT( fd_sess_handler_create( &new->sess_hdl, state_delete, ed_session_state_dump, NULL ) );
+	
+	/* Parse the configuration file */
+	CHECK_FCT( ed_conffile_parse(conffile, new) );
+	
+	if (TRACE_BOOL(FULL)) {
+		TRACE_DEBUG(INFO, "Echo/Drop plugin configuration ('%s'):", conffile);
+		struct fd_list * li;
+		
+		for (li = new->attributes.next; li != &new->attributes; li = li->next) {
+			struct ed_conf_attribute * eca = (struct ed_conf_attribute *)li;
+			char * act = (eca->action == ACT_ECHO) ? "ECHO" : "DROP";
+			if (eca->ext) {
+				fd_log_debug("  %s Code: %hhu, Vendor: %u, Ext-Type: %hu", act, eca->code, eca->vendor_id, eca->extype);
+				continue;
+			}
+			if (eca->tlv) {
+				fd_log_debug("  %s Code: %hhu, Vendor: %u, Type: %hhu", act, eca->code, eca->vendor_id, eca->type);
+				continue;
+			}
+			if (eca->vsa) {
+				fd_log_debug("  %s Code: %hhu, Vendor: %u", act, eca->code, eca->vendor_id);
+				continue;
+			}
+			fd_log_debug("  %s Code: %hhu", act, eca->code);
+		}
+	}
+	
+	/* OK, we are done */
+	*state = new;
+	return 0;
+}
+
+/* Destroy the state */
+static void ed_conf_free(struct rgwp_config * state)
+{
+	TRACE_ENTRY("%p", state);
+	CHECK_PARAMS_DO( state, return );
+	CHECK_FCT_DO( fd_sess_handler_destroy( &state->sess_hdl, NULL ),  );
+	while (! FD_IS_LIST_EMPTY(&state->attributes) ) {
+		struct fd_list * li = state->attributes.next;
+		fd_list_unlink(li);
+		free(li);
+	}
+	free(state);
+	return;
+}
+
+
+/* Handle attributes from a RADIUS request as specified in the configuration */
+static int ed_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+{
+	size_t nattr_used = 0;
+	int idx;
+	struct fd_list echo_list = FD_LIST_INITIALIZER(echo_list);
+	struct fd_list *li;
+	
+	TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
+	CHECK_PARAMS(cs && rad_req);
+	
+	/* For each attribute in the original message */
+	for (idx = 0; idx < rad_req->attr_used; idx++) {
+		int action = 0;
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+		
+		/* Look if we have a matching attribute in our configuration */
+		for (li = cs->attributes.next; li != &cs->attributes; li = li->next) {
+			struct ed_conf_attribute * eca = (struct ed_conf_attribute *)li;
+			uint32_t vid;
+			unsigned char * ptr;
+			
+			if (eca->code < attr->type)
+				continue;
+			if (eca->code > attr->type)
+				break;
+			
+			/* the code matches one in our configuration, check additional data if needed */
+			
+			if (! eca->vsa) {
+				action = eca->action;
+				break;
+			}
+			
+			if (attr->length < 8)
+				continue;
+			
+			ptr = (unsigned char *)(attr + 1);
+			/* since attr is not aligned, we may not access *(attr+1) directly */
+			memcpy(&vid, ptr, sizeof(uint32_t));
+			
+			if (eca->vendor_id < ntohl(vid))
+				continue;
+			if (eca->vendor_id > ntohl(vid))
+				break;
+			
+			/* The vendor matches our configured line... */
+			
+			if ( ! eca->tlv && ! eca->ext ) {
+				action = eca->action;
+				break;
+			}
+			
+			if (attr->length < 10)
+				continue;
+			
+			if (eca->tlv) {
+				struct radius_attr_vendor * tl = (struct radius_attr_vendor *)(ptr + sizeof(uint32_t));
+				if (tl->vendor_type == eca->type) {
+					action = eca->action;
+					break;
+				}
+				continue;
+			}
+			
+			if (eca->ext) {
+				/* To be done */
+				fd_log_debug("Extended attributes are not implemented yet!");
+				ASSERT(0);
+				continue;
+			}
+		}
+		
+		switch (action) {
+			case ACT_DROP:
+				TRACE_DEBUG(FULL, "Dropping attribute with code %hhd", attr->type);
+				break;
+				
+			case ACT_ECHO:
+				{
+					struct ed_saved_attribute * esa = NULL;
+					TRACE_DEBUG(FULL, "Saving attribute with code %hhd", attr->type);
+					CHECK_MALLOC( esa = malloc(sizeof(struct ed_saved_attribute) + attr->length - sizeof(struct radius_attr_hdr)) );
+					fd_list_init(&esa->chain, NULL);
+					memcpy(&esa->attr, attr, attr->length);
+					fd_list_insert_before(&echo_list, &esa->chain);
+				}
+				break;
+			
+			default: /* Attribute was not specified in the configuration */
+				/* We just keep the attribute in the RADIUS message */
+				rad_req->attr_pos[nattr_used++] = rad_req->attr_pos[idx];
+		}
+	}
+	rad_req->attr_used = nattr_used;
+	
+	/* Save the echoed values in the session, if any */
+	if (!FD_IS_LIST_EMPTY(&echo_list)) {
+		struct session * sess;
+		struct sess_state * st;
+		
+		CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_fw, &sess, NULL) );
+
+		CHECK_PARAMS_DO(sess,
+			{
+				fd_log_debug(	"[echodrop.rgwx] The extension is configured to echo some attributes from this message, but no session object has been created for it (yet)."
+						" Please check your configuration file and include a session-generating extension BEFORE calling echodrop.rgwx to echo attributes."
+						" Please use debug.rgwx to retrieve more information." );
+				return EINVAL;
+			} );
+		
+		/* Move the values in a dynamically allocated list */
+		CHECK_MALLOC( st = malloc(sizeof(struct sess_state)) );
+		fd_list_init(&st->sentinel, NULL);
+		fd_list_move_end(&st->sentinel, &echo_list);
+		
+		/* Save the list in the session */
+		CHECK_FCT( fd_sess_state_store( cs->sess_hdl, sess, &st ) );
+	}
+	
+	return 0;
+}
+
+/* Process an answer: add the ECHO attributes back, if any */
+static int ed_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+{
+	struct session * sess;
+	struct sess_state * st;
+	
+	TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
+	CHECK_PARAMS(cs);
+	
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
+	
+	/* If there is no session associated, just give up */
+	if (! sess ) {
+		TRACE_DEBUG(FULL, "No session associated with the message, nothing to do here...");
+		return 0;
+	}
+	
+	/* Now try and retrieve any data from the session */
+	CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, sess, &st ) );
+	if (st == NULL) {
+		/* No attribute saved in the session, just return */
+		return 0;
+	}
+	
+	/* From this point on, we have a list of attributes to add to the radius message */
+	
+	CHECK_PARAMS( rad_fw && *rad_fw);
+	
+	while (! FD_IS_LIST_EMPTY(&st->sentinel) ) {
+		struct ed_saved_attribute * esa = (struct ed_saved_attribute *)(st->sentinel.next);
+		
+		fd_list_unlink(&esa->chain);
+		
+		TRACE_DEBUG(FULL, "Echo attribute in the RADIUS answer: type %hhu, len: %hhu", esa->attr.type, esa->attr.length);
+		
+		/* Add this attribute in the RADIUS message */
+		CHECK_MALLOC( radius_msg_add_attr(*rad_fw, esa->attr.type, (unsigned char *)(esa + 1), esa->attr.length - sizeof(struct radius_attr_hdr)) );
+		
+		free(esa);
+	}
+	free(st);
+
+	return 0;
+}
+
+
+
+/* The exported symbol */
+struct rgw_api rgwp_descriptor = {
+	.rgwp_name       = "echo/drop",
+	.rgwp_conf_parse = ed_conf_parse,
+	.rgwp_conf_free  = ed_conf_free,
+	.rgwp_rad_req    = ed_rad_req,
+	.rgwp_diam_ans   = ed_diam_ans
+};	
diff --git a/extensions/app_radgw/rgwx_echodrop.h b/extensions/app_radgw/rgwx_echodrop.h
new file mode 100644
index 0000000..db84fbd
--- /dev/null
+++ b/extensions/app_radgw/rgwx_echodrop.h
@@ -0,0 +1,84 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/*  RADIUS translation plugin, to handle specific RADIUS attributes by either caching them and 
+adding them to a corresponding RADIUS reply, or just dropping the attributes (no conversion to Diameter) */
+/* This extension is a kind of swiss army-knife for interoperability, must be used with care. 
+  All attribute behaviors are specified through the configuration file of the extension
+ */
+
+#include "rgw_common.h"
+
+/* Action associated with an attribute */
+#define ACT_ECHO 1
+#define ACT_DROP 2
+
+/* Result of configuration parsing */
+struct rgwp_config {
+	/* list of attributes and associated actions */
+	struct fd_list attributes;
+	
+	/* Handler to store the echo'ed attributes values */
+	struct session_handler * sess_hdl;
+};
+
+/* An item of the attribute list */
+struct ed_conf_attribute {
+	struct fd_list 	chain;	/* link in the list */
+	
+	struct {
+		unsigned	action 	:2;	/* ACT_ECHO or ACT_DROP */
+		unsigned	vsa	:1;	/* Interpret as Vendor-Specific, and match the vendor id in addition to the code */
+		unsigned	tlv	:1;	/* Interpret as Vendor-Specific with TLV format, and match the type also */
+		unsigned	ext	:1;	/* Interpret as radius extended attribute, and match the ext-type also */
+	};
+	
+	uint32_t	vendor_id; /* vendor id to match when vsa = 1 */
+	uint16_t	extype; /* Ext-Type value to match if ext = 1 */
+	uint8_t		type; /* Type value to match if tlv = 1; */
+	
+	uint8_t		code; /* The attribute code, the list is ordered by this value */
+};
+
+/* For ECHO items, we save a list of these in the session */
+struct ed_saved_attribute {
+	struct fd_list		chain;
+	struct radius_attr_hdr  attr; /* copy of the attribute */
+	/* The data of the attribute follows ... */
+};
+
+
+/* The yacc parser */
+int ed_conffile_parse(char * conffile, struct rgwp_config *cs);
diff --git a/extensions/app_radgw/rgwx_echodrop.l b/extensions/app_radgw/rgwx_echodrop.l
new file mode 100644
index 0000000..aab2841
--- /dev/null
+++ b/extensions/app_radgw/rgwx_echodrop.l
@@ -0,0 +1,99 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Tokenizer for echo/drop plugin */
+
+%{
+#include "rgwx_echodrop.h"
+#include "rgwx_echodrop.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
+
+%%
+
+<*>\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 */
+
+[[:digit:]]+		{
+				/* Match an integer (not hexa) */
+				int ret = sscanf(yytext, "%d", &yylval->integer);
+				if (ret != 1) {
+					/* No matching: an error occurred */
+					fd_log_debug("[echodrop.rgwx] Unable to convert the value '%s' to a valid number: %s", yytext, strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+					/* Maybe we could REJECT instead of failing here? */
+				}
+				return INTEGER;
+			}
+				
+(?i:"echo")		{ return TOK_ECHO; 		}
+(?i:"drop")		{ return TOK_DROP; 		}
+(?i:"code")		{ return TOK_CODE; 		}
+(?i:"vendor")		{ return TOK_VENDOR; 		}
+(?i:"tlv")		{ return TOK_TLV; 		}
+(?i:"ext")		{ return TOK_EXT; 		}
+
+	/* Valid single characters for yyparse */
+<*>[;]			{ return yytext[0]; }
+
+
+	/* Unrecognized token */
+<*>[[:alnum:]]+		|	/* This rule is only useful to print a complete token in error messages */
+	/* Unrecognized character */
+<*>.			{ 
+				fd_log_debug("[echodrop.rgwx] Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext);
+			 	return LEX_ERROR; 
+			}
+
+%%
diff --git a/extensions/app_radgw/rgwx_echodrop.y b/extensions/app_radgw/rgwx_echodrop.y
new file mode 100644
index 0000000..fccaf06
--- /dev/null
+++ b/extensions/app_radgw/rgwx_echodrop.y
@@ -0,0 +1,255 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Yacc parser for echo/drop plugin. 
+See doc/echodrop.rgwx.conf.sample for description of the parsed format. */
+
+
+/* For development only : */
+%debug 
+%error-verbose
+
+/* The parser receives the configuration file filename and the conf structure as parameter */
+%parse-param {char * conffile}
+%parse-param {struct rgwp_config *cs}
+
+/* Keep track of location */
+%locations 
+%pure-parser
+
+%{
+#include "rgwx_echodrop.h"
+#include "rgwx_echodrop.tab.h"	/* bison is not smart enough to define the YYLTYPE before including this code, so... */
+
+
+/* Forward declaration */
+int yyparse(char * conffile, struct rgwp_config *cs);
+
+/* The Lex parser prototype */
+int rgwx_echodroplex(YYSTYPE *lvalp, YYLTYPE *llocp);
+
+
+/* Parse the configuration file */
+int ed_conffile_parse(char * conffile, struct rgwp_config *cs)
+{
+	extern FILE * rgwx_echodropin;
+	int ret;
+	
+	rgwx_echodropin = fopen(conffile, "r");
+	if ((rgwx_echodropin == NULL) && (*conffile != '/')) { /* We received a relative path, try adding DEFAULT_CONF_PATH prefix */
+		char * fullpath;
+		CHECK_MALLOC( fullpath = malloc( strlen(conffile) + strlen(DEFAULT_CONF_PATH) + 2 ) );
+		sprintf( fullpath, DEFAULT_CONF_PATH "/%s", conffile );
+		rgwx_echodropin = fopen(fullpath, "r");
+		free(fullpath);
+	}
+	if (rgwx_echodropin == NULL) {
+		ret = errno;
+		fd_log_debug("[echodrop.rgwx] Unable to open plugin configuration file %s for reading: %s", conffile, strerror(ret));
+		return ret;
+	}
+
+	ret = rgwx_echodropparse(conffile, cs);
+
+	fclose(rgwx_echodropin);
+
+	if (ret != 0) {
+		return EINVAL;
+	}
+	
+	return 0;
+}
+
+/* Function to report the errors */
+void yyerror (YYLTYPE *ploc, char * conffile, struct rgwp_config *cs, char const *s)
+{
+	if (ploc->first_line != ploc->last_line)
+		fd_log_debug("%s:%d.%d-%d.%d : %s", 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", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
+	else
+		fd_log_debug("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s);
+}
+
+static struct {
+	struct {
+		unsigned vendor :1;
+		unsigned tlv :1;
+		unsigned ext :1;
+	};
+	uint8_t		type;
+	uint16_t	extype;
+	uint32_t	vendor_id;
+} attrinfo;
+
+
+%}
+
+/* Values returned by lex for tokens */
+%union {
+	unsigned	integer;	/* Value  */
+}
+
+/* typed data */
+%token <integer> INTEGER
+%type  <integer> action
+
+/* simple tokens */
+%token		TOK_ECHO
+%token		TOK_DROP
+%token		TOK_CODE
+%token		TOK_VENDOR
+%token		TOK_TLV
+%token		TOK_EXT
+
+/* In case of error in the lexical analysis */
+%token 		LEX_ERROR
+
+
+/* -------------------------------------- */
+%%
+
+	/* The grammar definition */
+conffile:		/* empty grammar is OK */
+			| conffile attrdef
+			;
+
+	/* An attribute line */				
+attrdef:		{
+				memset(&attrinfo, 0, sizeof(attrinfo));
+			}
+			action TOK_CODE INTEGER vendordef ';'
+			{
+				struct ed_conf_attribute * new;
+				struct fd_list * li;
+
+				if ($4 >= 256) {
+					yyerror (&yylloc, conffile, cs, "Too big value for attribute CODE");
+					YYERROR;
+				}
+				
+				/* Create a new list item */
+				CHECK_MALLOC_DO( new = malloc(sizeof(struct ed_conf_attribute)), 
+					{
+						yyerror (&yylloc, conffile, cs, "Memory allocation error");
+						YYERROR;
+					} );
+				memset(new, 0, sizeof(struct ed_conf_attribute));
+				
+				fd_list_init(&new->chain, NULL);
+				
+				new->action = $2;
+				new->vsa = attrinfo.vendor;
+				new->tlv = attrinfo.tlv;
+				new->ext = attrinfo.ext;
+				
+				if (new->vsa)
+					new->vendor_id = attrinfo.vendor_id;
+				if (new->tlv)
+					new->type = attrinfo.type;
+				if (new->ext)
+					new->extype = attrinfo.extype;
+				
+				new->code = $4;
+				
+				/* Now place this attribute in the list */
+				for (li = cs->attributes.next; li != &cs->attributes; li = li->next) {
+					struct ed_conf_attribute *eca = (struct ed_conf_attribute *)li;
+					/* Order first by attribute code */
+					if (eca->code > new->code)
+						break;
+					if (eca->code < new->code)
+						continue;
+					
+					/* Then by VSA flag */
+					if (! new->vsa)
+						break;
+					if (! eca->vsa)
+						continue;
+					
+					/* Then by vendor value */
+					if (eca->vendor_id >= new->vendor_id)
+						break;
+				}
+				
+				fd_list_insert_before(li, &new->chain);
+			}
+			;
+	
+	/* What to do with the specified attribute */
+action:			TOK_ECHO
+			{
+				$$ = ACT_ECHO;
+			}
+			|
+			TOK_DROP
+			{
+				$$ = ACT_DROP;
+			}
+			;
+
+	/* Vendor specifics, if any */
+vendordef:		/* empty OK */
+			| TOK_VENDOR INTEGER specif
+			{
+				attrinfo.vendor_id = $2;
+				attrinfo.vendor = 1;
+			}
+			;
+			
+	/* Any additional specification ? */
+specif:			/* empty OK */
+			| TOK_TLV INTEGER
+			{
+				if ($2 >= (1 << 8)) {
+					yyerror (&yylloc, conffile, cs, "Too big value for TLV type");
+					YYERROR;
+				}
+				attrinfo.type = $2;
+				attrinfo.tlv = 1;
+			}
+			| TOK_EXT INTEGER
+			{
+				if ($2 >= (1 << 16)) {
+					yyerror (&yylloc, conffile, cs, "Too big value for Ext-Type");
+					YYERROR;
+				}
+				attrinfo.extype = $2;
+				attrinfo.ext = 1;
+				yyerror (&yylloc, conffile, cs, "The EXT option is not supported in this version.");
+				YYERROR;
+			}
+			;	
+
diff --git a/extensions/app_radgw/rgwx_sample.c b/extensions/app_radgw/rgwx_sample.c
new file mode 100644
index 0000000..4e2cd03
--- /dev/null
+++ b/extensions/app_radgw/rgwx_sample.c
@@ -0,0 +1,97 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Sample radius/diameter gateway plugin, for developers to see the structure of a plugin. */
+
+#include "rgw_common.h"
+
+/* The state of this extension */
+struct rgwp_config {
+	/* In a real extension, we would store the parsed configuration file, and the states of the extension */
+	int init;
+	
+	/* If needed to store information between sent Diameter request and received answer, the session is probably the best place.
+	  See rgwx_echodrop for an example. */
+};
+
+/* The function called at plugin initialization */
+static int sample_conf_parse ( char * conf_file, struct rgwp_config ** state )
+{
+	TRACE_ENTRY("%p %p", conf_file, state);
+	CHECK_PARAMS(state);
+	
+	CHECK_MALLOC( *state = malloc(sizeof(struct rgwp_config)) );
+	
+	(*state)->init = 1;
+	
+	return 0;
+}
+
+/* This function is called when the plugin is unloaded, to cleanup all the states */
+static void sample_conf_free(struct rgwp_config * state)
+{
+	TRACE_ENTRY("%p", state);
+	CHECK_PARAMS_DO( state, );
+	free(state);
+	return;
+}
+
+/* This function is called on incoming RADIUS messages. It should handle (some) RADIUS data and store into the Diameter message. */
+static int sample_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+{
+	TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
+	CHECK_PARAMS(cs);
+	TRACE_DEBUG(INFO, "RADIUS/Diameter Sample plugin received a new RADIUS message.");
+	return 0;
+}
+
+/* This function is called when a Diameter answer is coming back. It should remove the AVPs and add the attributes in the RADIUS message. */
+static int sample_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+{
+	TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
+	CHECK_PARAMS(cs);
+	TRACE_DEBUG(INFO, "RADIUS/Diameter Sample plugin received a new Diameter answer.");
+	return 0;
+}
+
+
+/* Finally, we declare the structure that will be loaded by main RADIUS/Diameter gateway extension */
+struct rgw_api rgwp_descriptor = {
+	.rgwp_name       = "sample",
+	.rgwp_conf_parse = sample_conf_parse,
+	.rgwp_conf_free  = sample_conf_free,
+	.rgwp_rad_req    = sample_rad_req,
+	.rgwp_diam_ans   = sample_diam_ans
+};	
diff --git a/extensions/app_radgw/rgwx_sip.c b/extensions/app_radgw/rgwx_sip.c
new file mode 100644
index 0000000..b4fd91e
--- /dev/null
+++ b/extensions/app_radgw/rgwx_sip.c
@@ -0,0 +1,855 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Alexandre Westfahl <awestfahl@freediameter.net>						 *
+*													 *												 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* Copyright (c) 2010, Alexandre Westfahl, Teraoka Laboratory (Keio University), and the WIDE Project. 	 *		
+*													 *
+* All rights reserved.											 *
+* Based on rgwx_auth plugin (Sebastien Decugis <sdecugis@freediameter.net>)					 *
+* 													 *
+* 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 Teraoka Laboratory nor the 							 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of Teraoka Laboratory 						 *
+*   													 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+
+/* RADIUS Access-Request messages translation plugin */
+
+#include "rgw_common.h"
+#include <string.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h> 
+
+/* Other constants we use */
+#define AI_SIP				6	/* Diameter SIP application */
+#define CC_MULTIMEDIA_AUTH_REQUEST	286	/* MAR */
+#define CC_MULTIMEDIA_AUTH_ANSWER	286	/* MAA */
+#define ACV_ASS_STATE_MAINTAINED	0	/* STATE_MAINTAINED */
+#define ACV_ASS_NO_STATE_MAINTAINED	1	/* NO_STATE_MAINTAINED */
+#define ER_DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED	2008
+#define ER_DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED	2006
+
+
+
+/* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
+#define CONV2DIAM_STR( _dictobj_ )	\
+	CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) );			\
+	/* Create the AVP with the specified dictionary model */			\
+	CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+	value.os.len = attr->length - sizeof(struct radius_attr_hdr);			\
+	value.os.data = (os0_t)(attr + 1);						\
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+	/* Add the AVP in the Diameter message. */					\
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+
+#define CONV2DIAM_STR_AUTH( _dictobj_ )	\
+	CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) );			\
+	/* Create the AVP with the specified dictionary model */			\
+	CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+	value.os.len = attr->length - sizeof(struct radius_attr_hdr);			\
+	value.os.data = (os0_t)(attr + 1);						\
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+	/* Add the AVP in the Diameter message. */					\
+	CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );			\
+		
+/* Same thing, for scalar AVPs of 32 bits */
+#define CONV2DIAM_32B( _dictobj_ )	\
+	CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\
+	CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
+	{										\
+		uint8_t * v = (uint8_t *)(attr + 1);					\
+		value.u32  = (v[0] << 24)						\
+			   | (v[1] << 16)						\
+			   | (v[2] <<  8)						\
+			   |  v[3] ;							\
+	}										\
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
+				
+				
+				
+				
+				
+/* The state we keep for this plugin */
+struct rgwp_config {
+	struct {
+		struct dict_object * Session_Id;		
+		struct dict_object * Auth_Application_Id;	
+		struct dict_object * Auth_Session_State;	
+		struct dict_object * Origin_Host;		
+		struct dict_object * Origin_Realm;		
+		struct dict_object * Destination_Realm;		
+		struct dict_object * SIP_AOR;			
+		struct dict_object * SIP_Method;			
+		struct dict_object * Destination_Host;		
+		struct dict_object * User_Name;			
+		struct dict_object * SIP_Server_URI;		
+		struct dict_object * SIP_Number_Auth_Items;	
+		struct dict_object * SIP_Authorization;	
+		struct dict_object * SIP_Authentication_Scheme;	
+		struct dict_object * SIP_Authentication_Info;	
+		struct dict_object * SIP_Auth_Data_Item;	
+		struct dict_object * Proxy_Info;		
+		struct dict_object * Route_Record;		
+		struct dict_object * Service_Type;		
+		struct dict_object * Result_Code;		
+		struct dict_object * Digest_URI;		
+		struct dict_object * Digest_Nonce;
+		struct dict_object * Digest_CNonce;
+		struct dict_object * Digest_Nonce_Count;				
+		struct dict_object * Digest_Realm;		
+		struct dict_object * Digest_Response;
+		struct dict_object * Digest_Method;
+		struct dict_object * Digest_Response_Auth;		
+		struct dict_object * Digest_Username;
+		struct dict_object * Digest_Algorithm;	
+		struct dict_object * Digest_QOP;
+
+		
+		
+	} dict; /* cache of the dictionary objects we use */
+	char * confstr;
+	//Chained list of nonce
+	struct fd_list listnonce;
+	//This will be used to lock access to chained list
+	pthread_mutex_t nonce_mutex; 
+};
+
+typedef struct noncechain noncechain;
+struct noncechain
+{
+	struct fd_list chain;
+    os0_t sid;
+    size_t sidlen;
+    os0_t nonce;
+    size_t noncelen;
+    
+};
+
+static int nonce_add_element(os0_t nonce, size_t noncelen, os0_t sid, size_t sidlen, struct rgwp_config * state)
+{
+	CHECK_PARAMS(nonce && state && sid && sidlen && noncelen);
+	
+	noncechain *newelt;
+	CHECK_MALLOC(newelt=malloc(sizeof(noncechain)));
+	
+	CHECK_MALLOC(newelt->nonce= os0dup(nonce, noncelen));
+	newelt->noncelen=noncelen;
+	
+	CHECK_MALLOC(newelt->sid=os0dup(sid, sidlen));
+	newelt->sidlen=sidlen;
+	
+	fd_list_init(&newelt->chain,NULL);
+	
+	CHECK_POSIX(pthread_mutex_lock(&state->nonce_mutex));
+	fd_list_insert_before(&state->listnonce,&newelt->chain);
+	CHECK_POSIX(pthread_mutex_unlock(&state->nonce_mutex));
+	
+	return 0;
+}
+/*
+static void nonce_del_element(char * nonce, struct rgwp_config *state)
+{
+	struct fd_list * li;
+	
+	CHECK_PARAMS_DO(nonce && state, return);
+	
+	for(li=state->listnonce.next;li!=&state->listnonce;li=li->next)
+	{
+		noncechain *temp=(noncechain *)li;
+		
+		if(strcmp(temp->nonce,nonce)==0)
+		{
+			fd_list_unlink (li);
+			free(temp->sid);
+			free(temp->nonce);
+			free(temp);
+			break;
+		}
+	}
+}
+*/
+//Retrieve sid from nonce
+static os0_t nonce_get_sid(os0_t nonce, size_t noncelen, size_t * sidlen, struct rgwp_config *state)
+{
+	struct fd_list * li;
+	os0_t sid=NULL;
+	
+	CHECK_PARAMS_DO(nonce && state && noncelen && sidlen, return NULL);
+	*sidlen=0;
+	
+	// **Start mutex
+	CHECK_POSIX_DO(pthread_mutex_lock(&state->nonce_mutex),); 
+	for(li=state->listnonce.next;li!=&state->listnonce;li=li->next)
+	{
+		noncechain *temp=(noncechain *)li;
+		
+		if (!fd_os_cmp(temp->nonce, temp->noncelen, nonce, noncelen))
+		{
+			fd_list_unlink (li);
+			sid=temp->sid;
+			*sidlen=temp->sidlen;
+			free(temp->nonce);
+			free(temp);
+			break;
+		}
+		
+	}
+	CHECK_POSIX_DO(pthread_mutex_unlock(&state->nonce_mutex),); 
+	// ***Stop mutex
+	return sid;
+}
+
+static void nonce_deletelistnonce(struct rgwp_config *state)
+{
+	// **Start mutex
+	CHECK_POSIX_DO(pthread_mutex_lock(&state->nonce_mutex),); 
+	while(!(FD_IS_LIST_EMPTY(&state->listnonce)) )
+	{
+		noncechain *temp=(noncechain *)state->listnonce.next;
+		
+		fd_list_unlink (&temp->chain);
+		free(temp->sid);
+		free(temp->nonce);
+		free(temp);
+		
+	}
+	CHECK_POSIX_DO(pthread_mutex_unlock(&state->nonce_mutex),); 
+	// ***Stop mutex
+}
+
+/* Initialize the plugin */
+static int sip_conf_parse(char * conffile, struct rgwp_config ** state)
+{
+	struct rgwp_config * new;
+	struct dict_object * app;
+	
+	
+	TRACE_ENTRY("%p %p", conffile, state);
+	CHECK_PARAMS( state );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
+	memset(new, 0, sizeof(struct rgwp_config));
+	
+	new->confstr = conffile;
+	
+	/* Resolve all dictionary objects we use */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &new->dict.Auth_Application_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Session-State", &new->dict.Auth_Session_State, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &new->dict.Origin_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &new->dict.Origin_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &new->dict.Destination_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-AOR", &new->dict.SIP_AOR, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Method", &new->dict.SIP_Method, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &new->dict.Destination_Host, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Server-URI", &new->dict.SIP_Server_URI, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Number-Auth-Items", &new->dict.SIP_Number_Auth_Items, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authorization", &new->dict.SIP_Authorization, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Auth-Data-Item", &new->dict.SIP_Auth_Data_Item, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authentication-Scheme", &new->dict.SIP_Authentication_Scheme, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authentication-Info", &new->dict.SIP_Authentication_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Proxy-Info", &new->dict.Proxy_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &new->dict.Route_Record, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &new->dict.Result_Code, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-URI", &new->dict.Digest_URI, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Nonce", &new->dict.Digest_Nonce, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Method", &new->dict.Digest_Method, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-CNonce", &new->dict.Digest_CNonce, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Nonce-Count", &new->dict.Digest_Nonce_Count, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Realm", &new->dict.Digest_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Response", &new->dict.Digest_Response, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Response-Auth", &new->dict.Digest_Response_Auth, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Username", &new->dict.Digest_Username, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Algorithm", &new->dict.Digest_Algorithm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-QoP", &new->dict.Digest_QOP, ENOENT) );
+
+
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Session Initiation Protocol (SIP) Application", &app, ENOENT) );
+	CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
+	
+	//chained list
+	fd_list_init(&new->listnonce,NULL);
+	CHECK_POSIX(pthread_mutex_init(&new->nonce_mutex,NULL));
+	
+	*state = new;
+	return 0;
+}
+
+/* deinitialize */
+static void sip_conf_free(struct rgwp_config * state)
+{
+	TRACE_ENTRY("%p", state);
+	CHECK_PARAMS_DO( state, return );
+	
+	nonce_deletelistnonce(state);
+	CHECK_POSIX_DO(pthread_mutex_destroy(&state->nonce_mutex), /*continue*/);
+	
+	free(state);
+	return;
+}
+
+/* Handle an incoming RADIUS request */
+static int sip_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
+{
+	int idx;
+	int got_AOR = 0;
+	int got_Dusername = 0;
+	int got_Drealm = 0;
+	int got_Duri = 0;
+	int got_Dmethod = 0;
+	int got_Dnonce = 0;
+	int got_Dresponse = 0;
+	int got_Dalgorithm = 0;
+	os0_t sid = NULL;
+	size_t sidlen;
+	os0_t un=NULL;
+	size_t  un_len;
+	size_t nattr_used = 0;
+	struct avp *auth_data=NULL, *auth=NULL, *avp = NULL;
+	union avp_value value;
+	struct session * sess;
+	
+	TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
+	
+	CHECK_PARAMS(rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
+	
+	/*
+	   RFC5090 RADIUS Extension Digest Application
+	*/
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_fw, &sess, NULL) );
+	if (sess != NULL) {
+		TRACE_DEBUG(INFO,"INTERNAL ERROR: We are not supposed to receive a session in radSIP plugin.");
+		return EINVAL;		
+	}
+	
+	/* Check basic information is there */
+	for (idx = 0; idx < rad_req->attr_used; idx++) {
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+		
+		
+		switch (attr->type) {
+			
+			case RADIUS_ATTR_USER_NAME:
+				if (attr->length>sizeof(struct radius_attr_hdr)) 
+				{
+					TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", (int)(attr->length- sizeof(struct radius_attr_hdr)), (char *)(attr+1));
+					un = (os0_t)(attr + 1);
+					un_len =attr->length - sizeof(struct radius_attr_hdr);
+				}
+			break;
+			case RADIUS_ATTR_DIGEST_USERNAME:
+				got_Dusername = 1;
+			break;
+			case RADIUS_ATTR_DIGEST_REALM:
+				got_Drealm = 1;
+			break;
+			case RADIUS_ATTR_DIGEST_URI:
+				got_Duri = 1;
+			break;
+			case RADIUS_ATTR_DIGEST_METHOD:
+				got_Dmethod = 1;
+			break;
+			// case RADIUS_ATTR_DIGEST_QOP:
+			// 	got_Dqop = 1;
+			// break;
+			// case RADIUS_ATTR_DIGEST_NONCE_COUNT:
+			// 	got_Dnonce_count = 1;
+			// break;
+			case RADIUS_ATTR_DIGEST_NONCE:
+				got_Dnonce = 1;
+				
+				sid=nonce_get_sid((os0_t)(attr+1), attr->length - sizeof(struct radius_attr_hdr), &sidlen, cs);
+				if(!sid)
+				{
+					TRACE_DEBUG(INFO,"We haven't found the session.'");
+					return EINVAL;
+				}
+				CHECK_FCT(fd_sess_fromsid_msg (sid, sidlen, &sess, NULL));
+				free(sid);
+							
+				
+			break;
+			// case RADIUS_ATTR_DIGEST_CNONCE:
+			// 	got_Dcnonce = 1;
+			// break;
+			case RADIUS_ATTR_DIGEST_RESPONSE:
+				got_Dresponse = 1;
+			break;			
+			case RADIUS_ATTR_DIGEST_ALGORITHM:
+				got_Dalgorithm = 1;
+			break;
+			case RADIUS_ATTR_SIP_AOR:
+				got_AOR = 1;
+			break;
+		}
+	}
+	if(!un)
+	{
+		TRACE_DEBUG(INFO,"No Username in request");
+		return EINVAL;
+	}
+
+	/* Create the session if it is not already done */
+	if (!sess) {
+		
+		DiamId_t fqdn;
+		size_t fqdn_len;
+		DiamId_t realm;
+		size_t realm_len;
+		
+		/* Get information on the RADIUS client */
+		CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdn_len, &realm, &realm_len) );
+		
+		/* Create a new Session-Id. The format is: {fqdn;hi32;lo32;username;diamid} */
+		CHECK_MALLOC( sid = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) );
+		sidlen = sprintf((char *)sid, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
+		CHECK_FCT( fd_sess_new(&sess, fqdn, fqdn_len, sid, sidlen) );
+		free(sid);
+	}
+	
+	/* Now, add the Session-Id AVP at beginning of Diameter message */
+	CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
+	value.os.data = sid;
+	value.os.len = sidlen;
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
+	
+	TRACE_DEBUG(FULL, "[sip.rgwx] Translating new message for session '%s'...", sid);
+	
+	/* Now add this session in the message */
+	CHECK_FCT( fd_msg_sess_set(*diam_fw, sess) );
+		
+	/* Add the Destination-Realm AVP */
+	CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
+	
+	int i = 0;
+	
+	/* Is there an '@' in the user name? We don't care for decorated NAI here */
+	for (i = un_len - 2; i > 0; i--) {
+		if (un[i] == '@') {
+			i++;
+			break;
+		}
+	}
+
+	if (i == 0) {
+		/* Not found in the User-Name => we use the local domain of this gateway */
+		value.os.data = (os0_t)fd_g_config->cnf_diamrlm;
+		value.os.len  = fd_g_config->cnf_diamrlm_len;
+	} else {
+		value.os.data = un + i;
+		value.os.len  = un_len - i;
+	}
+	
+	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	
+	/*
+	If the RADIUS Access-Request message does not
+	contain any Digest-* attribute, then the RADIUS client does not want
+	to apply HTTP Digest authentication, in which case, actions at the
+	gateway are outside the scope of this document.
+	*/
+	
+	if(!(got_Dmethod && got_Duri))
+	{
+		TRACE_DEBUG(INFO,"No Digest attributes in request, we drop it...");
+		return 1;
+	}
+
+	/* Add the appropriate command code & Auth-Application-Id */
+	{
+		struct msg_hdr * header = NULL;
+		CHECK_FCT( fd_msg_hdr ( *diam_fw, &header ) );
+		header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
+		header->msg_code = CC_MULTIMEDIA_AUTH_REQUEST;
+		header->msg_appl = AI_SIP;
+	
+	
+		/* Add the Auth-Application-Id */
+		{
+			CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Application_Id, 0, &avp ) );
+			value.i32 = header->msg_appl;
+			CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+			CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+		}
+	}
+	/*Add Auth_Session_State  AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Session_State, 0, &avp ) );
+		value.i32 = ACV_ASS_NO_STATE_MAINTAINED;
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	}
+
+	
+	/*Add SIP_Number_Auth_Items  AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Number_Auth_Items, 0, &avp ) );
+		value.i32 = 1; //We just treat one auth per request in gateway
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	}
+
+	/* Add SIP_Auth_Data_Item AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Auth_Data_Item, 0, &auth_data ) );
+	}
+	/* Add SIP_Authentication_Scheme AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Authentication_Scheme, 0, &avp ) );
+		value.i32=0; //There is only Digest Auth in RFC for now
+		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( fd_msg_avp_add ( auth_data, MSG_BRW_LAST_CHILD, avp) );
+	
+	}
+
+	
+	/* Add SIP_Authorization AVP */
+	{
+		CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Authorization, 0, &auth ) );
+		CHECK_FCT( fd_msg_avp_add ( auth_data, MSG_BRW_LAST_CHILD, auth) );
+	}
+
+	for (idx = 0; idx < rad_req->attr_used; idx++) 
+	{
+		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
+		
+		switch (attr->type) {
+
+			case RADIUS_ATTR_USER_NAME:
+			CONV2DIAM_STR( User_Name );
+			
+			if(!got_Dusername)
+			{
+				CONV2DIAM_STR_AUTH(Digest_Username);
+				got_Dusername=1;
+			}
+			
+			break;
+
+			case RADIUS_ATTR_DIGEST_URI:
+			
+			CONV2DIAM_STR_AUTH(Digest_URI);
+			
+			//All of these attributes are required by Diameter but not defined in RFC5090 so we provide FAKE values (only in first exchange)
+			if(!got_AOR)
+			{
+				CONV2DIAM_STR( SIP_AOR );
+				got_AOR=1;
+			}
+			/*
+			We must provide a fake nonce because of RFC4740 problem
+			TODO: remove when RFC is updated
+			==START of FAKE
+			*/
+			if(!got_Dresponse)
+			{
+				CONV2DIAM_STR_AUTH(Digest_Response);
+				got_Dresponse=1;
+			}
+			/*
+			==END of FAKE
+			*/
+			if(!got_Drealm)
+			{
+				//We extract Realm from Digest_URI
+				DiamId_t realm=NULL;
+				size_t realm_len = 0;
+				os0_t temp;
+				
+				temp = (os0_t)(attr + 1);
+				
+				for (i=attr->length - sizeof(struct radius_attr_hdr) - 1; i>=0; i--) {
+					if (temp[i] == '@') {
+						realm = (DiamId_t)temp + i + 1;
+						CHECK_FCT_DO( fd_os_validate_DiameterIdentity(&realm, &realm_len, 1),
+							realm = NULL );
+						break;
+					}
+				}
+			
+				if(realm!=NULL)
+				{
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Realm, 0, &avp ) );
+					value.os.data=(os0_t)realm;
+					value.os.len=realm_len;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );
+					
+					//We add SIP-Server-URI AVP because SIP server is registrar (through gateway)
+					CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Server_URI, 0, &avp ) );
+					value.os.data=(os0_t)realm;
+					value.os.len=realm_len;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+					CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+					
+					free(realm);
+				}
+				else
+				{
+					TRACE_DEBUG(INFO, "Can't extract domain from URI, droping request...");
+					return 1;
+				}	
+				got_Drealm=1;
+			}
+			break;
+
+			case RADIUS_ATTR_DIGEST_METHOD:
+			CONV2DIAM_STR(SIP_Method);
+			CONV2DIAM_STR_AUTH(Digest_Method);
+			break;
+			case RADIUS_ATTR_DIGEST_REALM:
+			CONV2DIAM_STR_AUTH(Digest_Realm);
+			
+			//We add SIP-Server-URI AVP because SIP server is registrar (through gateway)
+			CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Server_URI, 0, &avp ) );
+			os0_t temp;
+			#define SIP_PREFIX	"sip:"
+			size_t temp_len = attr->length - sizeof(struct radius_attr_hdr) + CONSTSTRLEN(SIP_PREFIX) + 1;
+			CHECK_MALLOC( temp = malloc(temp_len) );
+			temp_len = snprintf((char *)temp, temp_len, SIP_PREFIX "%.*s", (int)(attr->length - sizeof(struct radius_attr_hdr)), (char *)(attr + 1));
+			
+			value.os.data=temp;
+			value.os.len=temp_len;
+			
+			free(temp);
+			
+			CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+			CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+			break;
+			
+			case RADIUS_ATTR_DIGEST_USERNAME:
+			CONV2DIAM_STR_AUTH(Digest_Username);
+			break;
+				
+			case RADIUS_ATTR_DIGEST_QOP:
+			CONV2DIAM_STR_AUTH( Digest_QOP );
+			break;
+			case RADIUS_ATTR_DIGEST_ALGORITHM:		
+			CONV2DIAM_STR_AUTH( Digest_Algorithm );
+			break;
+			case RADIUS_ATTR_DIGEST_CNONCE:
+			CONV2DIAM_STR_AUTH( Digest_CNonce );
+			break;
+			case RADIUS_ATTR_DIGEST_NONCE:
+				CONV2DIAM_STR_AUTH( Digest_Nonce );
+			break;
+			case RADIUS_ATTR_DIGEST_NONCE_COUNT:
+			CONV2DIAM_STR_AUTH( Digest_Nonce_Count );
+			break;
+			case RADIUS_ATTR_DIGEST_RESPONSE:
+			CONV2DIAM_STR_AUTH( Digest_Response );
+			break;
+			case RADIUS_ATTR_SIP_AOR:
+			CONV2DIAM_STR( SIP_AOR );
+			break;
+				
+			default:
+			if(!got_Dalgorithm)
+			{
+				//[Note 3] If Digest-Algorithm is missing, 'MD5' is assumed.
+				#define DIGEST_ALGO_MD5	"MD5"
+										
+				CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Algorithm, 0, &avp ) );			
+										
+				value.os.data = (os0_t)DIGEST_ALGO_MD5;
+				value.os.len = CONSTSTRLEN(DIGEST_ALGO_MD5) - 1;
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );					
+				CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );	
+				got_Dalgorithm=1;	
+			}
+			
+			if(!got_Dnonce)
+			{
+				//We give a fake nonce because it will be calculated at the server.
+				CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Nonce, 0, &avp ) );
+				value.os.data=(unsigned char *)"nonce";
+				value.os.len=strlen((const char *)value.os.data);
+				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
+				CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );	
+				got_Dnonce=1;
+			}
+			break;
+	
+		}
+	}
+
+	
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, auth_data) );
+	
+	/* Update the radius message to remove all handled attributes */
+	rad_req->attr_used = nattr_used;
+
+	//fd_msg_dump_walk(1,*diam_fw);
+	
+	
+	return 0;
+}
+
+static int sip_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
+{
+	
+	
+	struct avp *avp, *next;
+	struct avp_hdr *ahdr;
+	//char buf[254]; /* to store some attributes values (with final '\0') */
+	struct session * sess;
+	os0_t sid = NULL;
+	size_t sidlen;
+	
+	TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
+	CHECK_PARAMS(cs && diam_ans && *diam_ans && rad_fw && *rad_fw);
+	
+	
+	/* MACROS to help in the process: convert AVP data to RADIUS attributes. */
+	/* Control large attributes:  _trunc_ = 0 => error; _trunc_ = 1 => truncate; _trunc = 2 => create several attributes */
+	#define CONV2RAD_STR( _attr_, _data_, _len_, _trunc_)	{					\
+		size_t __l = (size_t)(_len_);								\
+		size_t __off = 0;									\
+		TRACE_DEBUG(FULL, "Converting AVP to "#_attr_);						\
+		if ((_trunc_) == 0) {									\
+			CHECK_PARAMS( __l <= 253 );							\
+		}											\
+		if ((__l > 253) && (_trunc_ == 1)) {							\
+			TRACE_DEBUG(INFO, "[authSIP.rgwx] AVP truncated in "#_attr_);			\
+			__l = 253;									\
+		}											\
+		do {											\
+			size_t __w = (__l > 253) ? 253 : __l;						\
+			CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (_data_) + __off, __w));	\
+			__off += __w;									\
+			__l   -= __w;									\
+		} while (__l);										\
+	}
+
+	#define CONV2RAD_32B( _attr_, _data_)	{							\
+		uint32_t __v = htonl((uint32_t)(_data_));						\
+		TRACE_DEBUG(FULL, "Converting AVP to "#_attr_);						\
+		CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v)));	\
+	}
+
+	/* Search the different AVPs we handle here */
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
+	if (sess) {
+		CHECK_FCT( fd_sess_getsid(sess, &sid, &sidlen) );
+	}
+
+
+	/* Check the Diameter error code */
+	CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Result_Code, &avp) );
+	ASSERT( avp ); /* otherwise the message should have been discarded a lot earlier because of ABNF */
+	CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+	switch (ahdr->avp_value->u32) {
+		case ER_DIAMETER_MULTI_ROUND_AUTH:
+		case ER_DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED:		
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_CHALLENGE;
+			//struct timespec nowts;
+			//CHECK_SYS(clock_gettime(CLOCK_REALTIME, &nowts));
+			//nowts.tv_sec+=600;
+			//CHECK_FCT(fd_sess_settimeout(session, &nowts ));
+			break;
+		case ER_DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED:
+		case ER_DIAMETER_SUCCESS:
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_ACCEPT;
+			// in_success=1;
+			break;
+		
+		default:
+			(*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_REJECT;
+			fd_log_debug("[sip.rgwx] Received Diameter answer with error code '%d', session %.*s, translating into Access-Reject",
+					ahdr->avp_value->u32, (int)sidlen, sid);
+			return 0;
+	}
+	/* Remove this Result-Code avp */
+	CHECK_FCT( fd_msg_free( avp ) );
+	
+	/* Now loop in the list of AVPs and convert those that we know how */
+	CHECK_FCT( fd_msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) );
+	
+	while (next) {
+		int handled = 1;
+		avp = next;
+		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_WALK, &next, NULL) );
+		
+		CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+		
+		if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+			switch (ahdr->avp_code) {
+				
+				
+				case DIAM_ATTR_DIGEST_NONCE:
+					CONV2RAD_STR(DIAM_ATTR_DIGEST_NONCE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					nonce_add_element(ahdr->avp_value->os.data, ahdr->avp_value->os.len, sid, sidlen, cs);
+					break;
+				case DIAM_ATTR_DIGEST_REALM:
+					CONV2RAD_STR(DIAM_ATTR_DIGEST_REALM, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				case DIAM_ATTR_DIGEST_QOP:
+					CONV2RAD_STR(DIAM_ATTR_DIGEST_QOP, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;
+				case DIAM_ATTR_DIGEST_ALGORITHM:
+					CONV2RAD_STR(DIAM_ATTR_DIGEST_ALGORITHM, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
+					break;		
+				case DIAM_ATTR_DIGEST_RESPONSE_AUTH:
+					CONV2RAD_STR(DIAM_ATTR_DIGEST_RESPONSE_AUTH, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
+					break;
+				default:
+					handled=0;
+					break;
+			}
+		} 
+		else 
+		{
+			/* Vendor-specific AVPs */
+			switch (ahdr->avp_vendor) {
+				
+				default: /* unknown vendor */
+					handled = 0;
+			}
+		}
+		if (handled) {
+			CHECK_FCT( fd_msg_free( avp ) );
+		}
+	}
+	
+	return 0;
+}
+
+/* The exported symbol */
+struct rgw_api rgwp_descriptor = {
+	.rgwp_name       = "sip",
+	.rgwp_conf_parse = sip_conf_parse,
+	.rgwp_conf_free  = sip_conf_free,
+	.rgwp_rad_req    = sip_rad_req,
+	.rgwp_diam_ans   = sip_diam_ans
+};	
+