changeset 355:b811859f9963

Added extensions management in radius_gw
author Sebastien Decugis <sdecugis@nict.go.jp>
date Tue, 19 May 2009 13:34:51 +0900
parents e8f36f5342d1
children 555dc5a58aef
files extensions/radius_gw/CMakeLists.txt extensions/radius_gw/radius_gw.c extensions/radius_gw/radius_gw.h extensions/radius_gw/radius_gw.l extensions/radius_gw/radius_gw.y extensions/radius_gw/radius_gw_internal.h extensions/radius_gw/rgw_clients.c extensions/radius_gw/rgw_extensions.c extensions/radius_gw/rgw_utils.c extensions/radius_gw/sub_sample.c include/waaad/log-api.h include/waaad/waaad.h
diffstat 12 files changed, 1027 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/radius_gw/CMakeLists.txt	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/CMakeLists.txt	Tue May 19 13:34:51 2009 +0900
@@ -38,3 +38,8 @@
 # 		2865.tab.c
 # 		2865.tab.h)
 # 	ENDIF (BUILD_RADIUS_GW_2865)
+
+OPTION(BUILD_RADIUS_GW_SAMPLE "Build sample sub-extension? (for debug only)" ON)
+ 	IF (BUILD_RADIUS_GW_SAMPLE)
+ 	   ADD_LIBRARY(sub_sample MODULE ${RGW_DEFAULT_HEADER} sub_sample.c)
+ 	ENDIF (BUILD_RADIUS_GW_SAMPLE)
--- a/extensions/radius_gw/radius_gw.c	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/radius_gw.c	Tue May 19 13:34:51 2009 +0900
@@ -40,55 +40,38 @@
 #include "radius_gw_internal.h"
 
 int radius_gw_verbosity = 0;
+waaad_api_t * waaad_api = NULL;
 
 static int rgw_init(void)
 {
-	int ret = 0;
-	ret = rgw_servers_init();
-	if (ret != 0) {
-		TRACE_DEBUG(INFO, "Error in servers initialization: %s", strerror(ret));
-		return ret;
-	}
+	CHECK_FCT( rgw_servers_init() );
 	
-	ret = rgw_clients_init();
-	if (ret != 0) {
-		TRACE_DEBUG(INFO, "Error in clients initialization: %s", strerror(ret));
-		return ret;
-	}
+	CHECK_FCT( rgw_clients_init() );
 	
-	ret = rgw_extensions_init();
-	if (ret != 0) {
-		TRACE_DEBUG(INFO, "Error in extensions initialization: %s", strerror(ret));
-		return ret;
-	}
+	CHECK_FCT( rgw_extensions_init() );
 	
 	return 0;
 }
 
 static int rgw_entry(char * conffile) 
 {
-	int ret = 0;
+	CHECK_FCT( rgw_init() );
 	
-	ret = rgw_init();
-	if (ret != 0) {
-		log_error("Error in extension initialization: %s\n", strerror(ret));
-		return ret;
-	}
+	CHECK_FCT( rgw_conf_handle(conffile) );
 	
-	ret = rgw_conf_handle(conffile);
-	if (ret != 0) {
-		log_error("Error while parsin the configuration: %s\n", strerror(ret));
-		return ret;
-	}
-		
 	TRACE_DEBUG(FULL, "-------------------------");
 	rgw_servers_dump();
 	rgw_clients_dump();
 	rgw_extensions_dump();
 	TRACE_DEBUG(FULL, "-------------------------");
 	
+	/* Start making extension list accelerators */
+	rgw_extensions_start_cache();
+		
+	/* Start the servers */
+	CHECK_FCT( rgw_servers_start() );
 	
-	return ret;
+	return 0;
 }
 
 /* Unload */
@@ -99,4 +82,10 @@
 	rgw_clients_fini();
 }
 
-EXTENSION_API_INIT( API_MODULE_ALL, rgw_entry, "radius_gw" );
+/* EXTENSION_API_INIT( API_MODULE_ALL, rgw_entry, "radius_gw" ); */
+int waaad_ext_init(waaad_api_t * api, char * conffile) 
+{
+	EXTENSION_API_INIT_INTERN( API_MODULE_ALL, "radius_gw", api );
+	waaad_api = api;
+	return rgw_entry(conffile);
+}
--- a/extensions/radius_gw/radius_gw.h	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/radius_gw.h	Tue May 19 13:34:51 2009 +0900
@@ -35,16 +35,11 @@
 /* This file contains the description of the API between the base radius_gw extension and
   its extensions that add support for specific RADIUS attributes and/or commands.
   The "notes.txt" file contains the basic mechanism for use of this API. */
-  
+
+/* Note that waaad/waaad.h must be included before this file */  
 #ifndef _RADIUS_GW_H
 #define _RADIUS_GW_H
 
-/* This file extends definitions from the standard waaad API */
-#define IN_EXTENSION
-#define DEFINE_DEBUG_MACRO	radius_gw
-#include <waaad/waaad.h>
-
-
 /* This type is used for all lists in this extension */
 struct rgw_list {
 	struct rgw_list *next;
@@ -109,19 +104,18 @@
 
 #define RADIUS_GW_API_VER	1 /* increment when making changes to radius_gw_api definition bellow */
 struct radius_gw_api {
-	void *  (*rga_conf_parse_cb) ( char * conf_file );	/* configuration parser. Returns NULL on error only */
+	void *  (*rga_conf_parse_cb) ( char * conf_file );	/* configuration parser. Returns NULL on error only. Called even if no configuration file is passed (with NULL parameter then) */
 	void	(*rga_conf_free_cb) (void * conf); 		/* Free an object returned by previous cb */
 	
 	int	(*rga_rad_req_cb) ( void * conf, sess_id_t ** session, rad_t ** rad_req, msg_t ** diam_fw ); /* handle an incoming RADIUS message */
-	int	(*rga_diam_ans_cb) ( void * conf, sess_id_t ** session, msg_t ** diam_and, rad_t ** rad_fw ); /* handle the corresponding Diameter answer */
+	int	(*rga_diam_ans_cb) ( void * conf, sess_id_t ** session, msg_t ** diam_ans, rad_t ** rad_fw ); /* handle the corresponding Diameter answer */
 };
 
 /* All extensions must provide the following entry point that is called when the extension is loaded.
 Beware, the same extension may be loaded several times, and receive different configuration files. 
 No global data should be initialized during this function; instead it should be done during the rga_conf_parse_cb call,
 and store in the memory pointed by "conf" that is passed in turn to all callbacks. */
-extern int rga_register(int version, struct radius_gw_api * api);
-
+extern int rga_register(int version, waaad_api_t * waaad_api, struct radius_gw_api * api);
 
 
 /**************************************************************/
@@ -133,13 +127,128 @@
 void rgw_list_init(struct rgw_list * plist);
 int rgw_list_is_empty(struct rgw_list * plist);
 void rgw_list_insert_after(struct rgw_list * ref, struct rgw_list * item);
+void rgw_list_insert_before(struct rgw_list * ref, struct rgw_list * item);
 void rgw_list_unlink(struct rgw_list * plist);
 
 
+
+/****************************************/
+/*      Debug and related stuff         */
+/****************************************/
+
 /* Verbosity level for debug */
 extern int radius_gw_verbosity;
 
+/* CHECK_* macro from the daemon */
+/* Helper for tracing the CHECK_* macros bellow */
+#define TRACE_DEBUG_ALL( str ) 	\
+	TRACE_DEBUG(CALL, str );
 
 
+/* Macros to check a return value and branch out in case of error.
+ * These macro are to be used only when errors are highly improbable, not for expected errors.
+ */
+
+/* Check the return value of a system function and execute fallback in case of error */
+#define CHECK_SYS_DO( __call__, __fallback__  ) { 					\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( "Check SYS: " #__call__ );					\
+	__ret__ = (__call__);								\
+	if (__ret__ < 0) {								\
+		int __err__ = errno;	/* We may handle EINTR here */			\
+		log_error("An unexpected error occured (%s), turn on debug for detail\n",\
+			strerror(__err__));						\
+		TRACE_DEBUG(INFO, "Error in '" #__call__ "': %s", strerror(__err__));	\
+		__fallback__;								\
+	}										\
+}
+/* Check the return value of a system function, return error code on error */
+#define CHECK_SYS( __call__  ) { 							\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( "Check SYS: " #__call__ );					\
+	__ret__ = (__call__);								\
+	if (__ret__ < 0) {								\
+		int __err__ = errno;	/* We may handle EINTR here */			\
+		log_error("An unexpected error occured (%s), turn on debug for detail\n",\
+			strerror(__err__));						\
+		TRACE_DEBUG(INFO, "Error in '" #__call__ "': %s", strerror(__err__));	\
+		return __err__;								\
+	}										\
+}
+
+/* Check the return value of a POSIX function and execute fallback in case of error or special value */
+#define CHECK_POSIX_DO2( __call__, __val__, __fallback1__, __fallback2__ ) {			\
+	int __ret__;										\
+	TRACE_DEBUG_ALL( "Check POSIX: " #__call__ );						\
+	__ret__ = (__call__);									\
+	if (__ret__ != 0) {									\
+		if (__ret__ == (__val__)) {							\
+			__fallback1__;								\
+		} else {									\
+			log_error("An unexpected error, see log for detail\n");			\
+			TRACE_DEBUG(INFO, "Error in '" #__call__ "': %s", strerror(__ret__));	\
+			__fallback2__;								\
+		}										\
+	}											\
+}
+
+/* Check the return value of a POSIX function and execute fallback in case of error */
+#define CHECK_POSIX_DO( __call__, __fallback__ ) 					\
+	CHECK_POSIX_DO2( (__call__), 0, , __fallback__ );
+
+/* Check the return value of a POSIX function and return it if error */
+#define CHECK_POSIX( __call__ ) { 							\
+	int __v__;									\
+	CHECK_POSIX_DO( __v__ = (__call__), return __v__ );				\
+}
+
+/* Check that a memory allocator did not return NULL, otherwise log an error and execute fallback */
+#define CHECK_MALLOC_DO( __call__, __fallback__ ) { 					\
+	void *  __ret__;								\
+	TRACE_DEBUG_ALL( "Check MALLOC: " #__call__ );					\
+	__ret__ = (void *)( __call__ );							\
+	if (__ret__ == NULL) {								\
+		int __err__ = errno;							\
+		log_error("Memory allocation failed: %s\n", strerror(__err__));		\
+		TRACE_DEBUG(INFO, "Error in '" #__call__ "': %s", strerror(__err__));	\
+		__fallback__;								\
+	}										\
+}
+
+/* Check that a memory allocator did not return NULL, otherwise return ENOMEM */
+#define CHECK_MALLOC( __call__ )							\
+	CHECK_MALLOC_DO( __call__, return ENOMEM );
+
+
+/* The next functions can be used also for expected errors */
+
+/* Check parameters at function entry, execute fallback on error */
+#define CHECK_PARAMS_DO( __bool__, __fallback__ )					\
+	TRACE_DEBUG_ALL( "Check PARAMS: " #__bool__ );					\
+	if ( ! (__bool__) ) {								\
+		TRACE_DEBUG(INFO, "Invalid parameter received in " #__bool__ );		\
+		__fallback__;								\
+	}
+/* Check parameters at function entry, return EINVAL if the boolean is false (similar to assert) */
+#define CHECK_PARAMS( __bool__ )							\
+	CHECK_PARAMS_DO( __bool__, return EINVAL );
+
+/* Check the return value of an internal function, log and propagate */
+#define CHECK_FCT_DO( __call__, __fallback__ ) {					\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( "Check FCT: " #__call__ );					\
+	__ret__ = (__call__);								\
+	if (__ret__ != 0) {								\
+		TRACE_DEBUG(INFO, "Error in '" #__call__ "': %s", strerror(__ret__));	\
+		__fallback__;								\
+	}										\
+}
+/* Check the return value of a function call, return any error code */
+#define CHECK_FCT( __call__ ) {								\
+	int __v__;									\
+	CHECK_FCT_DO( __v__ = (__call__), return __v__ );				\
+}
+		
+
 #endif /* _RADIUS_GW_H */
   
--- a/extensions/radius_gw/radius_gw.l	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/radius_gw.l	Tue May 19 13:34:51 2009 +0900
@@ -131,6 +131,9 @@
 				return INTEGER;
 			}
 
+<IN_EXT>[:]		{ return yytext[0]; }
+
+
 	/* Client section */
 (?i:"cli")		{ BEGIN(IN_CLI1); return CLI_PREFIX; 		}
 
--- a/extensions/radius_gw/radius_gw.y	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/radius_gw.y	Tue May 19 13:34:51 2009 +0900
@@ -48,9 +48,6 @@
 %locations 
 %pure-parser
 
-/* Not very sure if the two shift/reduce conflicts are normal... well... */
-%expect 2
-
 %{
 #include "radius_gw_internal.h"
 #include "radius_gw.tab.h"	/* bison is not smart enough to define the YYLTYPE before including this code, so... */
@@ -145,7 +142,9 @@
 	buf_sz = 0;
 	buf_rsz = 0;
 }
-	
+
+static int port = 0;
+static char * extconffile = NULL;
 
 %}
 
@@ -162,8 +161,6 @@
 %token <ss>	IP
 
 %type <string>	FILENAME
-%type <string>	extconf
-%type <integer> extport
 
 /* simple tokens */
 %token		DISABLED
@@ -219,48 +216,43 @@
 			;
 
 extension:		{
+				/* Reset the parameters */
 				buf_reinit();
+				port = RGW_EXT_PORT_AUTH | RGW_EXT_PORT_ACCT ;
+				free(extconffile); extconffile = NULL;
 			}
-			EXT_PREFIX '=' FILENAME extconf extport extcodes ';'
+			EXT_PREFIX '=' FILENAME ext_attributes ';'
 			{
 				/* Add this extension in the list */
-				if ( rgw_extensions_add( $4, $5, $6, buf, buf_sz ) ) {
+				if ( rgw_extensions_add( $4, extconffile, port, &buf, buf_sz ) ) {
 					yyerror (&yylloc, conffile, "Error parsing / adding extension !");
 					YYERROR;
 				}
 				
 				/* Free the array */
 				buf_reinit();
-			}
-			;
-			
-extconf:		/* empty */
-			{ 
-				$$ = NULL;
-			}
-			| ':' QSTRING
-			{
-				$$ = $2;
+				
+				/* stop conffile from being freed here */
+				extconffile = NULL;
 			}
 			;
-			
-extport:		/* empty */
+
+ext_attributes:		/* empty */
+			| ext_attributes ':' QSTRING
 			{
-				$$ = RGW_EXT_PORT_AUTH | RGW_EXT_PORT_ACCT ;
-			}
-			| ':' AUTH
-			{
-				$$ = RGW_EXT_PORT_AUTH;
+				extconffile = $3;
 			}
-			| ':' ACCT
+			| ext_attributes ':' AUTH
 			{
-				$$ = RGW_EXT_PORT_ACCT;
+				port = RGW_EXT_PORT_AUTH;
 			}
+			| ext_attributes ':' ACCT
+			{
+				port = RGW_EXT_PORT_ACCT;
+			}
+			| ext_attributes ':' extcodes_list
+			;
 
-extcodes:		/* empty */
-			| ':' extcodes_list
-			;
-			
 extcodes_list:		/* empty */
 			| extcodes_list INTEGER
 			{
@@ -283,7 +275,7 @@
 			CLI_PREFIX '=' IP '/' clisecret_key ';'
 			{
 				/* Add this client */
-				if ( rgw_clients_add( $4, buf, buf_sz ) ) {
+				if ( rgw_clients_add( $4, &buf, buf_sz ) ) {
 					yyerror (&yylloc, conffile, "Error parsing / adding client !");
 					YYERROR;
 				}
--- a/extensions/radius_gw/radius_gw_internal.h	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/radius_gw_internal.h	Tue May 19 13:34:51 2009 +0900
@@ -37,6 +37,11 @@
 #ifndef _RADIUS_GW_INTERNAL_H
 #define _RADIUS_GW_INTERNAL_H
 
+/* This file extends definitions from the standard waaad API */
+#define IN_EXTENSION
+#define DEFINE_DEBUG_MACRO	radius_gw
+#include <waaad/waaad.h>
+
 /* include the general stuff */
 #include "radius_gw.h"
 
@@ -50,7 +55,8 @@
 #define ASSERT(x) assert(x)
 #endif /* ASSERT */
 
-
+/* API definition of waaad */
+extern waaad_api_t * waaad_api;
     
 /* The RADIUS server(s) interface */
 struct rgw_serv {
@@ -78,7 +84,7 @@
 
 /* The clients allowed to connect to these servers */
 int rgw_clients_init(void);
-int rgw_clients_add( struct sockaddr * ip_port, unsigned char * key, size_t keylen );
+int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen );
 void rgw_clients_dump(void);
 void rgw_clients_fini(void);
 
@@ -87,8 +93,9 @@
 #define RGW_EXT_PORT_AUTH	1
 #define RGW_EXT_PORT_ACCT	2
 int rgw_extensions_init(void);
-int rgw_extensions_add( char * extfile, char * conffile, int port, unsigned char * codes_array, size_t codes_sz );
+int rgw_extensions_add( char * extfile, char * conffile, int port, unsigned char ** codes_array, size_t codes_sz );
 void rgw_extensions_dump(void);
+void rgw_extensions_start_cache(void);
 void rgw_extensions_fini(void);
 
 /* The function to parse the configuration file */
--- a/extensions/radius_gw/rgw_clients.c	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/rgw_clients.c	Tue May 19 13:34:51 2009 +0900
@@ -37,20 +37,354 @@
 
 #include "radius_gw_internal.h"
 
-int rgw_clients_init(void)
+/* Ordered lists of clients. The order relationship is a memcmp on the address zone. 
+   For same addresses, the port is compared.
+   A given address cannot be added with a 0-port and another port value.
+ */
+static struct rgw_list cli_ip, cli_ip6;
+
+/* Mutex to protect the previous lists */
+static pthread_mutex_t cli_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/* Structure describing one client */
+struct cli_info {
+	struct rgw_list		chain;
+	union {
+		struct sockaddr_in	*sin;
+		struct sockaddr_in6	*sin6;
+	};
+	struct {
+		unsigned char * data;
+		size_t		len;
+	} 			key;
+	int			refcount;
+};
+
+/* Function to alloc a new cli_info */
+static int cli_info_create(struct cli_info ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen )
 {
+	struct cli_info *tmp = NULL;
+	
+	/* Create the new object */
+	CHECK_MALLOC( tmp = malloc(sizeof (struct cli_info)) );
+	memset(tmp, 0, sizeof(struct cli_info));
+	
+	/* Initialize the chain */
+	rgw_list_init(&tmp->chain);
+	
+	/* move the sa info reference */
+	if ((*ip_port)->sa_family == AF_INET)
+		tmp->sin = (struct sockaddr_in *) (*ip_port);
+	else
+		tmp->sin6 = (struct sockaddr_in6 *) (*ip_port);
+	*ip_port = NULL;
+	
+	/* move the key material */
+	tmp->key.data = *key;
+	tmp->key.len = keylen;
+	*key = NULL;
+	
+	/* Done! */
+	*res = tmp;
 	return 0;
 }
 
-int rgw_clients_add( struct sockaddr * ip_port, unsigned char * key, size_t keylen )
+
+/* Decrease refcount on a client; the lock must be held when this function is called. */
+void rgw_clients_unlink(struct cli_info * client)
+{
+	client->refcount -= 1;
+	
+	if (client->refcount <= 0) {
+		/* to be sure */
+		ASSERT( rgw_list_is_empty(&client->chain) );
+		
+		/* Free the data */
+		free(client->sin);
+		free(client->key.data);
+		free(client);
+	}
+}
+
+/* Increase refcount on a client; the lock must be held when this function is called. */
+void rgw_clients_link(struct cli_info * client)
+{
+	client->refcount += 1;
+}
+
+
+/* Function to look for an existing cli_info, or the previous element. 
+   The cli_mtx must be held when calling this func. 
+   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 cli_info_search(struct cli_info ** res, struct sockaddr * ip_port )
+{
+	int ret = 0;
+	int cmp;
+	struct rgw_list *ref = NULL;
+	
+	CHECK_PARAMS(res && ip_port);
+	
+	switch (ip_port->sa_family) {
+		case AF_INET: {
+			struct sockaddr_in * sin = (struct sockaddr_in *)ip_port;
+			
+			for (ref = cli_ip.next; ref != &cli_ip; ref = ref->next) {
+				cmp = memcmp(&sin->sin_addr, &((struct cli_info *)ref)->sin->sin_addr, sizeof(struct in_addr));
+				
+				if (cmp > 0 )
+					continue; /* we must search further in the list */
+				
+				if (cmp < 0 ) /* This IP is not in the list, we return the previous element */
+					break;
+				
+				/* Now we found an entry with the same IP; let's check the port number if both are present */
+				
+				/* If the ip_port we are searching does not contain a port, just return the first match result */
+				/* If the entry in the list does not contain a port, return it as a match */
+				/* If both ports are equal, it is a match */
+				if ((sin->sin_port == 0) 
+						|| (((struct cli_info *)ref)->sin->sin_port == 0) 
+						|| (sin->sin_port == ((struct cli_info *)ref)->sin->sin_port)) {
+					*res = (struct cli_info *)ref;
+					return EEXIST;
+				}
+				
+				/* now we order on the port value (we don't care aboit the network or host byte order there */
+				if (sin->sin_port > ((struct cli_info *)ref)->sin->sin_port)
+					continue;
+				else
+					break;
+			}
+			
+			*res = (struct cli_info *)(ref->prev);
+			return ENOENT;
+		}
+		break;
+		
+		case AF_INET6: {
+			struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *)ip_port;
+			
+			/* Exact same logic as for IP */
+			for (ref = cli_ip6.next; ref != &cli_ip6; ref = ref->next) {
+				cmp = memcmp(&sin6->sin6_addr, &((struct cli_info *)ref)->sin6->sin6_addr, sizeof(struct in6_addr));
+				if (cmp > 0 )
+					continue;
+				if (cmp < 0 )
+					break;
+				if ((sin6->sin6_port == 0) 
+						|| (((struct cli_info *)ref)->sin6->sin6_port == 0) 
+						|| (sin6->sin6_port == ((struct cli_info *)ref)->sin6->sin6_port)) {
+					*res = (struct cli_info *)ref;
+					return EEXIST;
+				}
+				if (sin6->sin6_port > ((struct cli_info *)ref)->sin6->sin6_port)
+					continue;
+				else
+					break;
+			}
+			*res = (struct cli_info *)(ref->prev);
+			return ENOENT;
+		}
+	}
+	
+	/* We're never supposed to reach this point */
+	ASSERT(0);
+	return EINVAL;
+}
+
+static void cli_key_dump(char * keydump /* 60 bytes */, char * key, size_t keylen)
 {
-	return ENOTSUP;
+	int i, j, idx;
+	char * ptr;
+
+	memset(keydump, 0, 60);
+	idx = 0;
+
+	for (i = 0; i < 2; i++) {
+		for (j = 0; j < 8; j++) {
+			if (idx >= keylen)
+				break;
+
+			if (j == 4) {
+				*keydump = ' '; keydump++;
+			}
+
+			sprintf(keydump, "%02hhX ", key[idx]);
+			keydump += 3;
+			idx ++;
+		}
+		
+		*keydump = '\t'; keydump++;
+	}
+	if (keylen > idx) {
+		sprintf(keydump, "...");
+	}
+}
+
+
+
+int rgw_clients_init(void)
+{
+	rgw_list_init(&cli_ip);
+	rgw_list_init(&cli_ip6);
+	return 0;
+}
+
+int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen )
+{
+	struct cli_info * prev = NULL, *new = NULL;
+	int ret;
+	
+	TRACE_ENTRY("%p %p %lu", ip_port, key, keylen);
+	
+	CHECK_PARAMS( ip_port && key && *key && keylen );
+	CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) );
+	
+	/* Dump the entry in debug mode */
+	if (TRACE_BOOL(FULL + 1 )) {
+		char ipstr[INET6_ADDRSTRLEN];
+		char keydump[60];
+		uint16_t port;
+		
+		if (ip_port->sa_family == AF_INET) {
+			inet_ntop(AF_INET, &((struct sockaddr_in *)ip_port)->sin_addr,ipstr,sizeof(ipstr));
+			port = ntohs(((struct sockaddr_in *)ip_port)->sin_port);
+		} else {
+			inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ip_port)->sin6_addr,ipstr,sizeof(ipstr));
+			port = ntohs(((struct sockaddr_in6 *)ip_port)->sin6_port);
+		}
+		
+		cli_key_dump(&keydump[0], *key, keylen);
+		
+		TRACE_DEBUG(FULL, "Adding client [%s]:%hu with %d bytes key: %s", ipstr, port, keylen, keydump);
+	}
+	
+	/* Lock the lists */
+	CHECK_POSIX( pthread_mutex_lock(&cli_mtx) );
+	
+	/* Check if the same entry does not already exist */
+	ret = cli_info_search(&prev, ip_port );
+	if (ret == ENOENT) {
+		CHECK_FCT_DO( ret = cli_info_create( &new, &ip_port, key, keylen ), goto end );
+		
+		rgw_list_insert_after(&prev->chain, &new->chain);
+		
+		rgw_clients_link(new);
+		
+		ret = 0;
+		goto end;
+	}
+	
+	if (ret == EEXIST) {
+		/* Check if the key is the same, then skip or return an error */
+		if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) )) {
+			TRACE_DEBUG(INFO, "Skipping duplicate client description");
+			goto end;
+		}
+		
+		TRACE_DEBUG(INFO, "Error: conflicting entries");	
+		log_error("Error adding a RADIUS client: conflict with a previous entry.\n");
+		{
+			char ipstr[INET6_ADDRSTRLEN];
+			char keydump[60];
+			uint16_t port;
+
+			if (prev->sin->sin_family == AF_INET) {
+				inet_ntop(AF_INET, &prev->sin->sin_addr,ipstr,sizeof(ipstr));
+				port = ntohs(prev->sin->sin_port);
+			} else {
+				inet_ntop(AF_INET6, &prev->sin6->sin6_addr,ipstr,sizeof(ipstr));
+				port = ntohs(prev->sin6->sin6_port);
+			}
+
+			cli_key_dump(&keydump[0], prev->key.data, prev->key.len);
+
+			log_error( "Previous entry: [%s]:%hu, key (%db): %s\n", ipstr, port, prev->key.len, keydump);
+			
+			if (ip_port->sa_family == AF_INET) {
+				inet_ntop(AF_INET, &((struct sockaddr_in *)ip_port)->sin_addr,ipstr,sizeof(ipstr));
+				port = ntohs(((struct sockaddr_in *)ip_port)->sin_port);
+			} else {
+				inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ip_port)->sin6_addr,ipstr,sizeof(ipstr));
+				port = ntohs(((struct sockaddr_in6 *)ip_port)->sin6_port);
+			}
+
+			cli_key_dump(&keydump[0], *key, keylen);
+			log_error( "New entry:      [%s]:%hu, key (%db): %s\n", ipstr, port, keylen, keydump);			
+		}
+	}
+end:
+	/* Lock the lists */
+	CHECK_POSIX( pthread_mutex_unlock(&cli_mtx) );
+	
+	return ret;
 }
 
 void rgw_clients_dump(void)
 {
+	struct cli_info * client = NULL;
+	struct rgw_list *ref = NULL;
+	char ipstr[INET6_ADDRSTRLEN];
+	char keydump[60];
+	uint16_t port;
+	
+	if ( ! TRACE_BOOL(FULL) )
+		return;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ );
+	
+	log_debug(" RADIUS IP clients list:\n");
+	for (ref = cli_ip.next; ref != &cli_ip; ref = ref->next) {
+		
+		client = (struct cli_info *)ref;
+		
+		inet_ntop(AF_INET, &client->sin->sin_addr,ipstr,sizeof(ipstr));
+		port = ntohs(client->sin->sin_port);
+		
+		cli_key_dump(&keydump[0], client->key.data, client->key.len);
+		
+		log_debug("   [%s]:%hu, %d bytes: %s\n", ipstr, port, client->key.len, keydump);
+	}
+		
+	log_debug(" RADIUS IPv6 clients list:\n");
+	for (ref = cli_ip6.next; ref != &cli_ip6; ref = ref->next) {
+		
+		client = (struct cli_info *)ref;
+		
+		inet_ntop(AF_INET6, &client->sin6->sin6_addr,ipstr,sizeof(ipstr));
+		port = ntohs(client->sin6->sin6_port);
+		
+		cli_key_dump(&keydump[0], client->key.data, client->key.len);
+		
+		log_debug("   [%s]:%hu, %d bytes: %s\n", ipstr, port, client->key.len, keydump);
+	}
+		
+	CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), /* ignore error */ );
 }
+
 void rgw_clients_fini(void)
 {
+	struct rgw_list * client;
+	
+	TRACE_ENTRY();
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ );
+	
+	/* empty the lists */
+	while ( ! rgw_list_is_empty(&cli_ip) ) {
+		client = cli_ip.next;
+		rgw_list_unlink(client);
+		rgw_clients_unlink((struct cli_info *)client);
+	}
+	while (! rgw_list_is_empty(&cli_ip6)) {
+		client = cli_ip6.next;
+		rgw_list_unlink(client);
+		rgw_clients_unlink((struct cli_info *)client);
+	}
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), /* ignore error */ );
+	
 }
 
--- a/extensions/radius_gw/rgw_extensions.c	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/rgw_extensions.c	Tue May 19 13:34:51 2009 +0900
@@ -36,21 +36,379 @@
 /* Manage the list of sub-extensions that provide handlers for RADIUS messages / attributes */
 
 #include "radius_gw_internal.h"
+#include <dlfcn.h>
+#include <libgen.h>
+
+/* List of extensions, in the order they are written in the configuration file. */
+static struct rgw_list ext_list;
+
+/* Description of an extension entry */
+struct ext_descr {
+	struct rgw_list		chain; 	/* chaining in the ext_list list */
+	
+	void * 			dlo;	/* object returned by dlopen of the extension, to use with dlclose later */
+	struct radius_gw_api	api;	/* the callbacks registered by rga_register */
+	void *			conf;	/* the parsed configuration returned by rga_conf_parse_cb */
+	
+	int			port;	/* this extension is called for messages received on this(these) port(s) only */
+	unsigned char *		cc;	/* array of command codes, or NULL for any cc */
+	size_t			cc_len; /* size of the previous array */
+	
+	char *			extname; /* baseame of the extension, for debug messages. To be freed when object is detroyed */
+	char * 			conffile; /* configuration file passed to the extension, or "(null)". To be freed when object is destroyed */
+};
+
+/* Accelerators for each command code (one for each port). These accelerators are built on-demand, as a cache, after start_cache function has been called.  */
+static struct rgw_list ext_accel_auth, ext_accel_acct;
+
+/* accelerator list, one per command code */
+struct ext_accel {
+	struct rgw_list		chain;	/* link in the ext_accel_* list */
+	unsigned char		ccode;	/* the command code of this accelerator. The previous list is ordered according to this value. We don't handle extended CC yet */
+	struct rgw_list		extensions; /* head for the list of extensions to be called for this command code. List of ext_accel_item items */
+};
+
+/* accelerator item, references to extensions */
+struct ext_accel_item {
+	struct rgw_list		chain; 	/* link in the ext_accel "extensions" list */
+	struct ext_descr *	ext;	/* pointer to the extension data */
+};
+
+/* Mutex to protect all the previous lists */
+static pthread_mutex_t ext_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/* Has start_cache been called? */
+static int cache_started;
+
+
+/* The lock must be held before calling this function */
+static int get_accelerator(struct rgw_list ** list, unsigned char ccode, int port)
+{
+	struct rgw_list *refer, *search;
+	struct ext_accel * accel = NULL;
+	struct ext_accel_item * item = NULL;
+	
+	TRACE_ENTRY("%p %hhu %i", list, ccode, port);
+	
+	CHECK_PARAMS( cache_started && list && ((port == RGW_EXT_PORT_AUTH) || (port == RGW_EXT_PORT_ACCT)) );
+	
+	if (port == RGW_EXT_PORT_AUTH)
+		refer = &ext_accel_auth;
+	else
+		refer = &ext_accel_acct;
+	
+	/* Check if we have already an accelerator for this ccode */
+	for (search = refer->next; search != refer; search = search->next) {
+		struct ext_accel * loc = (struct ext_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->extensions;
+		return 0;
+	}
+	
+	/* We must create the accelerator list, then save it just before search */
+	
+	CHECK_MALLOC( accel = malloc(sizeof(struct ext_accel)) );
+	memset(accel, 0, sizeof(struct ext_accel) );
+	rgw_list_init(&accel->chain);
+	rgw_list_init(&accel->extensions);
+	accel->ccode = ccode;
+	
+	/* Check if each extension from the global list is enabled for this port and ccode */
+	for (refer = ext_list.next; refer != &ext_list; refer = refer->next) {
+		struct ext_descr * loc = (struct ext_descr *)refer;
+		
+		/* Skip if this extension is not registered for this port */
+		if (! (loc->port & port) )
+			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 is called for this port / ccode, add to the accelerator */
+		CHECK_MALLOC( item = malloc(sizeof(struct ext_accel_item)) );
+		memset(item, 0, sizeof(struct ext_accel_item));
+		rgw_list_init(&item->chain);
+		item->ext = loc;
+		/* Add as last element of the accelerator */
+		rgw_list_insert_before(&accel->extensions, &item->chain);
+	}
+	
+	/* Now, save this accelerator entry in the global list */
+	rgw_list_insert_before(search, &accel->chain);
+	
+	return 0;
+}
+
 
 int rgw_extensions_init(void)
 {
+	TRACE_ENTRY();
+	
+	cache_started = 0;
+	rgw_list_init(&ext_list);
+	rgw_list_init(&ext_accel_auth);
+	rgw_list_init(&ext_accel_acct);
+	
 	return 0;
 }
 
-int rgw_extensions_add( char * extfile, char * conffile, int port, unsigned char * codes_array, size_t codes_sz )
+int rgw_extensions_add( char * extfile, char * conffile, int port, unsigned char ** codes_array, size_t codes_sz )
 {
-	return ENOTSUP;
+	struct ext_descr * new;
+	int (* ext_rga_register)(int version, waaad_api_t * waaad, struct radius_gw_api * api);
+	int ret = 0;
+	char * myextfile = NULL;
+	
+	TRACE_ENTRY();
+	
+	CHECK_PARAMS( extfile && port && codes_array && (cache_started == 0) );
+	
+	CHECK_MALLOC( myextfile = strdup(extfile) );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct ext_descr)) );
+	memset(new, 0, sizeof(struct ext_descr));
+	
+	rgw_list_init(&new->chain);
+	
+	/* Copy some names to be freed when object is destroyed */
+	CHECK_MALLOC( new->extname = strdup(basename(myextfile)) );
+	free(myextfile);
+	CHECK_MALLOC( new->conffile = conffile ? conffile : strdup("(null)") );
+	
+	/* Try and load the extension */
+	TRACE_DEBUG(INFO, "Loading subextension: %s", extfile);
+	new->dlo = dlopen(extfile, RTLD_LAZY | RTLD_LOCAL);
+	if (new->dlo == NULL) {
+		/* An error occured */
+		log_error("Loading of subextension %s failed:\n %s\n", extfile, dlerror());
+		goto error;
+	}
+	
+	/* Resolve the entry point */
+	ext_rga_register = dlsym( new->dlo, "rga_register" );
+	if (ext_rga_register == NULL) {
+		/* An error occured */
+		log_error("Unable to resolve 'rga_register' in subextension %s:\n %s\n", extfile, dlerror());
+		goto error;
+	}
+	
+	/* Call the entry point */
+	TRACE_DEBUG(FULL, "Calling subextension entry point...");
+	CHECK_FCT_DO(  (*ext_rga_register) (RADIUS_GW_API_VER, waaad_api, &new->api),  goto error  );
+	
+	/* Now parse the configuration file, this will initialize all extension states and store it in the returned pointer (the subextensions must be re-entrant) */
+	TRACE_DEBUG(FULL, "Parsing subext conf file: %s", new->conffile );
+	new->conf = (*(new->api.rga_conf_parse_cb))(conffile);
+	if (new->conf == NULL) {
+		log_error("An error occurred while parsing configuration parameter for extension '%s' (%s), aborting...\n", new->extname, new->conffile);
+		goto error;
+	}
+	
+	/* Now order the array 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->port = port;
+	
+	/* And save this new extension in the list */
+	CHECK_POSIX( pthread_mutex_lock(&ext_mtx) );
+	rgw_list_insert_before(&ext_list, &new->chain);
+	CHECK_POSIX( pthread_mutex_unlock(&ext_mtx) );
+	
+	return 0;
+	
+	
+error:
+	if (new && new->dlo)
+		dlclose(new->dlo);
+	if (new)
+		free(new);	
+	return EINVAL;
 }
 
 void rgw_extensions_dump(void)
 {
+	struct ext_descr * ext;
+	struct rgw_list * ptr, *ptraccel;
+	
+	if ( ! TRACE_BOOL(FULL) )
+		return;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&ext_mtx), );
+	if ( ! rgw_list_is_empty( &ext_list ) )
+		log_debug(" RADIUS gateway registered sub-extensions:\n");
+	
+	for (ptr = ext_list.next; ptr != &ext_list; ptr = ptr->next) {
+		char * codes = NULL;
+		
+		ext = (struct ext_descr *)ptr;
+		if (ext->cc) {
+			int i;
+			char * nxt;
+			
+			CHECK_MALLOC_DO( codes = malloc(ext->cc_len * 3 + 1), break );
+			nxt = codes;
+			for (i = 0; i < ext->cc_len; i++) {
+				sprintf(nxt, "%02hhx ", ext->cc[i]);
+				nxt += 3;
+			}
+		}
+		
+		log_debug("  %-15s (%-20s) - p: %s %s, %d cc: %s\n", 
+				ext->extname, 
+				ext->conffile, 
+				ext->port & RGW_EXT_PORT_AUTH ? "Au" : "  ",
+				ext->port & RGW_EXT_PORT_ACCT ? "Ac" : "  ",
+				ext->cc_len,
+				ext->cc ? codes : "*");
+		
+		free(codes);
+	}
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&ext_mtx), );
+	
+	
+	/* Dump the list of accelerators */
+	if ( ! TRACE_BOOL(FULL + 1) )
+		return;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&ext_mtx), );
+	if ( !rgw_list_is_empty( &ext_accel_auth ) || !rgw_list_is_empty( &ext_accel_acct ))
+		log_debug(" RADIUS gateway sub-extensions accelerators:\n");
+	
+	for (ptraccel = ext_accel_auth.next; ptraccel != &ext_accel_auth; ptraccel = ptraccel->next) {
+		struct ext_accel * accel = (struct ext_accel *)ptraccel;
+		log_debug("  auth, code %02hhu:\n", accel->ccode);
+
+		for (ptr = accel->extensions.next; ptr != &accel->extensions; ptr = ptr->next) {
+			struct ext_accel_item * item = (struct ext_accel_item *)ptr;
+			log_debug("     %-15s (%s)\n", item->ext->extname, item->ext->conffile);
+		}
+	}
+	for (ptraccel = ext_accel_acct.next; ptraccel != &ext_accel_acct; ptraccel = ptraccel->next) {
+		struct ext_accel * accel = (struct ext_accel *)ptraccel;
+		log_debug("  acct, code %02hhu:\n", accel->ccode);
+
+		for (ptr = accel->extensions.next; ptr != &accel->extensions; ptr = ptr->next) {
+			struct ext_accel_item * item = (struct ext_accel_item *)ptr;
+			log_debug("     %-15s (%s)\n", item->ext->extname, item->ext->conffile);
+		}
+	}
+	
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&ext_mtx), );
+	
+}
+
+void rgw_extensions_start_cache(void)
+{
+	cache_started++;
+	
+	/* tmp: for debug purpose
+	{
+		struct rgw_list *mylist = NULL;
+		CHECK_FCT_DO( get_accelerator(&mylist, 4, RGW_EXT_PORT_AUTH),  );
+		CHECK_FCT_DO( get_accelerator(&mylist, 4, RGW_EXT_PORT_ACCT),  );
+		CHECK_FCT_DO( get_accelerator(&mylist, 8, RGW_EXT_PORT_AUTH),  );
+		CHECK_FCT_DO( get_accelerator(&mylist, 8, RGW_EXT_PORT_ACCT),  );
+		CHECK_FCT_DO( get_accelerator(&mylist, 0x0b, RGW_EXT_PORT_AUTH),  );
+		CHECK_FCT_DO( get_accelerator(&mylist, 0x0b, RGW_EXT_PORT_ACCT),  );
+		rgw_extensions_dump();
+	}
+	*/
 }
 
 void rgw_extensions_fini(void)
 {
+	struct rgw_list * item, *subitem;
+	
+	TRACE_ENTRY();
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&ext_mtx), );
+	
+	/* Remove all elements from all accelerators */
+	while ( ! rgw_list_is_empty(&ext_accel_auth) ) {
+		item = ext_accel_auth.next;
+		rgw_list_unlink(item);
+		{
+			struct ext_accel * accel = (struct ext_accel *)item;
+			while ( ! rgw_list_is_empty(&accel->extensions) ) {
+				subitem = accel->extensions.next;
+				rgw_list_unlink(subitem);
+				free(subitem);
+			}
+		}
+		free(item);
+	}
+	while ( ! rgw_list_is_empty(&ext_accel_acct) ) {
+		item = ext_accel_acct.next;
+		rgw_list_unlink(item);
+		{
+			struct ext_accel * accel = (struct ext_accel *)item;
+			while ( ! rgw_list_is_empty(&accel->extensions) ) {
+				subitem = accel->extensions.next;
+				rgw_list_unlink(subitem);
+				free(subitem);
+			}
+		}
+		free(item);
+	}
+	
+	/* Now destroy all extensions */
+	while ( ! rgw_list_is_empty(&ext_list) ) {
+		struct ext_descr * ext = (struct ext_descr *) ext_list.next;
+		rgw_list_unlink(&ext->chain);
+		free(ext->conffile);
+		free(ext->extname);
+		free(ext->cc);
+		if (ext->conf)
+			(*ext->api.rga_conf_free_cb)(ext->conf);
+		dlclose(ext->dlo);
+		free(ext);
+	}
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&ext_mtx), );
 }
--- a/extensions/radius_gw/rgw_utils.c	Thu May 14 16:19:47 2009 +0900
+++ b/extensions/radius_gw/rgw_utils.c	Tue May 19 13:34:51 2009 +0900
@@ -63,6 +63,18 @@
 	ref->next = item;
 }
 
+void rgw_list_insert_before(struct rgw_list * ref, struct rgw_list * item)
+{
+	ASSERT(ref != NULL);
+	ASSERT(item != NULL);
+	ASSERT(rgw_list_is_empty(item));
+	item->prev = ref->prev;
+	item->next = ref;
+	item->head = ref->head;
+	ref->prev->next = item;
+	ref->prev = item;
+}
+
 void rgw_list_unlink(struct rgw_list * plist)
 {
 	ASSERT(plist != NULL);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/radius_gw/sub_sample.c	Tue May 19 13:34:51 2009 +0900
@@ -0,0 +1,105 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2008, 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 sub extension for debug */
+
+#define IN_EXTENSION
+#define DEFINE_DEBUG_MACRO	sub_sample
+#define DECLARE_API_POINTERS
+#include <waaad/waaad.h>
+
+#include "radius_gw.h"
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+int sub_sample_verbosity = 2;
+
+struct sample_state {
+	char * conffile;
+};
+
+static void * sample_conf_parse(char * conffile)
+{
+	struct sample_state * ret = NULL;
+	
+	TRACE_ENTRY("%p", conffile);
+	
+	CHECK_MALLOC_DO( ret = malloc(sizeof(struct sample_state)), return NULL );
+	
+	ret->conffile = conffile;
+	
+	return ret;
+}
+
+static void sample_conf_free(void * conf)
+{
+	TRACE_ENTRY("%p", conf);
+	CHECK_PARAMS_DO( conf, );
+	free(conf);
+	return;
+}
+
+static int sample_rad_req(void * conf, sess_id_t ** session, rad_t ** rad_req, msg_t ** diam_fw )
+{
+	TRACE_ENTRY("%p %p %p %p", conf, session, rad_req, diam_fw);
+	return ENOTSUP;
+}
+
+static int sample_diam_ans(void * conf, sess_id_t ** session, msg_t ** diam_ans, rad_t ** rad_fw )
+{
+	TRACE_ENTRY("%p %p %p %p", conf, session, diam_ans, rad_fw);
+	return ENOTSUP;
+}
+
+int rga_register(int version, waaad_api_t * waaad_api, struct radius_gw_api * api)
+{
+	TRACE_ENTRY("%d %p", version, api);
+	CHECK_PARAMS( waaad_api && api );
+	
+	if (version != RADIUS_GW_API_VER) {
+		log_error("ABI version mismatch, please recompile this extension (%s)\n", __FILE__);
+		return EINVAL;
+	}
+	
+	EXTENSION_API_INIT_INTERN( API_MODULE_ALL, "sub_sample", waaad_api );
+	
+	api->rga_conf_parse_cb = sample_conf_parse;
+	api->rga_conf_free_cb  = sample_conf_free;
+	api->rga_rad_req_cb    = sample_rad_req;
+	api->rga_diam_ans_cb   = sample_diam_ans;
+	
+	return 0;
+}
--- a/include/waaad/log-api.h	Thu May 14 16:19:47 2009 +0900
+++ b/include/waaad/log-api.h	Tue May 19 13:34:51 2009 +0900
@@ -205,6 +205,19 @@
 #define __agr( arg1, arg2 ) arg1 ## arg2
 #define _aggregate( arg1, arg2 ) __agr( arg1, arg2 )
 
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+#  define __func__ __FUNCTION__
+# else /* __GNUC__ >= 2 */
+#  define __func__ "<unknown>"
+# endif /* __GNUC__ >= 2 */
+#endif /* __STDC_VERSION__ < 199901L */
+
+#ifndef __PRETTY_FUNCTION__
+#define __PRETTY_FUNCTION__ __func__
+#endif /* __PRETTY_FUNCTION__ */
+
+
 /* Macro to set the current thread name, useful for multi-threaded environments */
 #define THREAD_NAME( name )		\
 	log_set_thread_name( (name), _stringize(DEFINE_DEBUG_MACRO) );
@@ -213,20 +226,20 @@
 #define TRACE_BOOL(level) ( level <= local_debug_level + _aggregate(DEFINE_DEBUG_MACRO, _verbosity) )
 
 /* The general debug macro, each call results in two lines of debug messages */
-#define TRACE_DEBUG(level,format,args... ) {										\
-	if ( TRACE_BOOL(level) ) {											\
-		char __buf[25];												\
-		char * __thn = ((char *)pthread_getspecific(thread_name) ?: "unnamed");					\
-		log_debug("\t |%-30s\t%s\tin %s@%s:%d\n"								\
-				"\t%s|%*s" format "\n",  								\
-						__thn, log_time(__buf, sizeof(__buf)), __FUNCTION__, __FILE__, __LINE__,\
-						(level < FULL)?"@":" ",level, "", ## args); 				\
-	}														\
+#define TRACE_DEBUG(level,format,args... ) {											\
+	if ( TRACE_BOOL(level) ) {												\
+		char __buf[25];													\
+		char * __thn = ((char *)pthread_getspecific(thread_name) ?: "unnamed");						\
+		log_debug("\t | th:%-30s\t%s\tin %s@%s:%d\n"									\
+			  "\t%s|%*s" format "\n",  										\
+						__thn, log_time(__buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,	\
+						(level < FULL)?"@":" ",level, "", ## args); 					\
+	}															\
 }
 
 /* Helper for function entry */
 #define TRACE_ENTRY(_format,_args... ) \
-	TRACE_DEBUG(FCTS, "->%s (" #_args ") = (" _format ") >", __FUNCTION__, ##_args );
+	TRACE_DEBUG(FCTS, "->%s (" #_args ") = (" _format ") >", __PRETTY_FUNCTION__, ##_args );
 
 /* Helper for debugging by adding traces */
 #define TRACE_HERE()	\
--- a/include/waaad/waaad.h	Thu May 14 16:19:47 2009 +0900
+++ b/include/waaad/waaad.h	Tue May 19 13:34:51 2009 +0900
@@ -126,9 +126,7 @@
 #define API_MODULE_SECURITY	0x00000100	/* Load the security API in the extension */
 
 
-#define EXTENSION_API_INIT(flags, function, name) 								\
-														\
-int waaad_ext_init(waaad_api_t * api, char * conffile) {							\
+#define EXTENSION_API_INIT_INTERN( flags, name, api ) {								\
 	sub_api_header_t * hdr;											\
 	char * buffer = (char *)api;										\
 	int index = 0;												\
@@ -279,7 +277,14 @@
 														\
 	/* next module... */											\
 														\
-	return (function)(conffile);										\
+}
+
+
+#define EXTENSION_API_INIT(flags, function, name) 		\
+								\
+int waaad_ext_init(waaad_api_t * api, char * conffile) {	\
+	EXTENSION_API_INIT_INTERN( flags, name, api );		\
+	return (function)(conffile);				\
 }														
 		
 
"Welcome to our mercurial repository"