changeset 352:6d22078428a5

Wrote initial conf parser for radius_gw ... to debug
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 08 May 2009 18:28:41 +0900
parents 932ed12e1821
children 6ca21598562a
files doc/radius_gw.conf.sample extensions/radius_gw/CMakeLists.txt extensions/radius_gw/notes.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/radius_msg.c
diffstat 9 files changed, 767 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/doc/radius_gw.conf.sample	Thu May 07 11:01:46 2009 +0900
+++ b/doc/radius_gw.conf.sample	Fri May 08 18:28:41 2009 +0900
@@ -18,22 +18,23 @@
 
 # Extensions are registered either for every message, or by port (auth or acct), or by port and code.
 # The general format is:
-# extension [: conf_file [: port] [: code(s)]] ;
+# ext = extension [: conf_file] [: port] [: code(s)] ;
 #  Where:
-#    extension is the file name (relative or absolute) of the extension to load.
-#    conf_file is optional and the name of the configuration file.
-#    port is optional, either "auth" or "acct". 
+#    extension is the quoted file name (relative or absolute) of the extension to load.
+#    conf_file is optional and the quoted name of the configuration file.
+#    port is optional, either auth or acct. 
 #       If not specified, extension is called for messages incoming on both ports
-#    code(s): coma-separated list of command codes for which this extension must be called.
+#    code(s): space-separated list of command codes for which this extension must be called.
 #       If not specified, the extension is called for all incoming messages.
+#       The values are interpreted as hexadecimal.
 #
 # The extensions are called in the order they appear in this file.
 # Here are some explained examples:
-#  3579.so;   Load this extension and call it for all messages. No configuration file.
-#  3579.so : 3579.conf;  Same as previous but with a configuration file specified.
-#  3579.so : : auth; No configuration file, but called only for messages on authentication port.
-#  3579.so : : 4, 8, 11;  Called for messages with command code 4, 8, or 11 only.
-#  3579.so : 3579.conf : auth : 4, 8, 11;  All parameters combined.
+#  ext = "3579.so";   Load this extension and call it for all messages. No configuration file.
+#  ext = "3579.so" : "3579.conf";  Same as previous but with a configuration file specified.
+#  ext = "3579.so" : auth; No configuration file, but called only for messages on authentication port.
+#  ext = "3579.so" : 4 8 b;  Called for messages with command code 4, 8, or 11 only.
+#  ext = "3579.so" : "3579.conf" : auth : 4 8 b;  All parameters combined.
 
 # Once the list of extensions for an incoming message has been called (or empty list), 
 # if some of the attributes have not been handled, an error is loggued.
@@ -43,17 +44,14 @@
 # RADIUS Clients #
 ##################
 
-# Each RADIUS client must be declared in the form: IP = shared-secret ;
+# Each RADIUS client must be declared in the form: cli = IP / shared-secret ;
 # IP can be ipv4 or ipv6
+# port can be additionaly restricted with brackets: IP[port] (ex: 192.168.0.1[1812])
 # shared-secret can be a quoted string, or a list of hexadecimal values.
 # examples:
-# 192.168.100.1 = "secret key" ; # the shared secret buffer is 0x736563726574206b6579 (length 10 bytes)
-# fe00::1 = 73 65 63 72 65 74 20 6b 65 79; # same shared secret as previously
+# cli = 192.168.100.1 / "secret key" ; # the shared secret buffer is 0x736563726574206b6579 (length 10 bytes)
+# cli = fe00::1 / 73 65 63 72 65 74 20 6b 65 79; # same shared secret as previously
 # When a packet is received from an IP not declared here, it is silently discarded.
-#
-# Alternatively, a separate file can be specified in the form:
-# radius_client_file = "/path/to/file";
-# The format of this file in that case is yet to be defined (option not supported fully yet)
 
 
 ####################
@@ -71,7 +69,7 @@
 # auth_server_ip4 = 0.0.0.0;
 
 # The IPv6 address to which the server is bound, or "disable"
-# auth_server_ipv6 = :: ;
+# auth_server_ip6 = :: ;
 
 
 ################
@@ -88,4 +86,4 @@
 # acct_server_ip4 = 0.0.0.0;
 
 # The IPv6 address to which the server is bound, or "disable"
-# acct_server_ipv6 = :: ;
+# acct_server_ip6 = :: ;
--- a/extensions/radius_gw/CMakeLists.txt	Thu May 07 11:01:46 2009 +0900
+++ b/extensions/radius_gw/CMakeLists.txt	Fri May 08 18:28:41 2009 +0900
@@ -12,6 +12,7 @@
 	lex.radius_gw.c
 	radius_gw.tab.c
 	radius_gw.tab.h
+	radius_msg.c
 )
 
 SET( RGW_DEFAULT_HEADER
--- a/extensions/radius_gw/notes.txt	Thu May 07 11:01:46 2009 +0900
+++ b/extensions/radius_gw/notes.txt	Fri May 08 18:28:41 2009 +0900
@@ -44,3 +44,10 @@
    - Diameter answer (with linked Diameter Request inside)
  - When all extensions have been called, the RADIUS answer is generated, with appropriate authenticator and all, and 
    sent to the RADIUS client.
+
+*** About sessions ***
+
+The session is created the first time an Access-Request is received.
+Then a State attribute in the form "Diameter/..." is used to store the information.
+More details in http://tools.ietf.org/html/rfc4005#section-9
+
--- a/extensions/radius_gw/radius_gw.c	Thu May 07 11:01:46 2009 +0900
+++ b/extensions/radius_gw/radius_gw.c	Fri May 08 18:28:41 2009 +0900
@@ -32,3 +32,44 @@
 * 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 radius_gw extension.
+ */
+
+#define DECLARE_API_POINTERS
+#include "radius_gw_internal.h"
+
+static int rgw_init(void)
+{
+	rgw_conf_init();
+}
+
+static int rgw_entry(char * conffile) 
+{
+	int ret = 0;
+	
+	ret = rgw_init();
+	if (ret != 0) {
+		log_error("Error in extension initialization: %s\n", strerror(ret));
+		return ret;
+	}
+	
+	ret = rgw_conf_handle(conffile);
+	if (ret != 0) {
+		log_error("Error while parsin the configuration: %s\n", strerror(ret));
+		return ret;
+	}
+	
+	
+	rgw_dump_conf();
+	
+	return ret;
+}
+
+/* Unload */
+void waaad_ext_fini(void)
+{
+	;
+}
+
+EXTENSION_API_INIT( API_MODULE_ALL, rgw_entry, "radius_gw" );
--- a/extensions/radius_gw/radius_gw.h	Thu May 07 11:01:46 2009 +0900
+++ b/extensions/radius_gw/radius_gw.h	Fri May 08 18:28:41 2009 +0900
@@ -40,6 +40,7 @@
 #define _RADIUS_GW_H
 
 /* This file extends definitions from the standard waaad API */
+#define IN_EXTENSION
 #include <waaad/waaad.h>
 
 /* This type is used for all lists in this extension */
--- a/extensions/radius_gw/radius_gw.l	Thu May 07 11:01:46 2009 +0900
+++ b/extensions/radius_gw/radius_gw.l	Fri May 08 18:28:41 2009 +0900
@@ -32,3 +32,253 @@
 * 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 "radius_gw_internal.h"
+#include "radius_gw.tab.h"
+
+/* Update the column information */
+#ifdef DEBUG_LEX
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+	log_debug("(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'\n",\
+		yylloc->first_line, yylloc->first_column, 		\
+		yylloc->last_line, yylloc->last_column, 		\
+		yy_act, yyleng, yytext); 				\
+}
+#else /* DEBUG_LEX */
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+}
+#endif
+
+/* %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_EXT
+%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) {
+					log_error("Unable to allocate memory: %s\n", strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+				}
+
+				yylval->string[yyleng-2] = '\0';
+				
+				/* the yacc parser will check the string is valid */
+				return QSTRING;
+			}
+			
+
+	/* Extension section */			
+(?i:"ext")		{ BEGIN(IN_EXT); return EXT_PREFIX; 		}
+
+<IN_EXT>(?i:"auth")	{ return AUTH; }
+<IN_EXT>(?i:"acct")	{ return ACCT; }
+
+<IN_EXT,IN_CLI2>[[:xdigit:]]+	{
+				/* Convert this to an integer value */
+				int ret = sscanf(yytext, "%x", &yylval->integer);
+				if (ret != 1) {
+					/* No matching: an error occurred */
+					log_error("Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+					/* Maybe we could REJECT instead of failing here? */
+				}
+				return INTEGER;
+			}
+
+	/* Client section */
+(?i:"cli")		{ BEGIN(IN_CLI1); return CLI_PREFIX; 		}
+
+	/* Match an IP (4 or 6) and optional port */
+<IN_CLI1>({IP4}|{IP6}){BR_PORT}?	{
+				char * work;
+				char * port;
+				unsigned short p = 0;
+				
+				work = strdup(yytext);
+				if ( work == NULL ) {
+					log_error("Unable to allocate memory: %s\n", strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+				}
+				
+				if (port = strchr(work, '[')) {
+					*port = '\0';
+					port++;
+					if (sscanf(port, "%hu]", &p) != 1) {
+						log_error("'%s' is not a valid port: %s\n", port, strerror(errno));
+						free(work);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+				}
+				
+				/* Do we have an IP or IPv6? Let's check if we have ':' char somewhere in the beginning */
+				if (memchr(work, ':', 5) != NULL) {
+					struct sockaddr_in6 * sin6 = NULL;
+				
+					sin6 = malloc(sizeof(struct sockaddr_in6));
+					if (sin6 == NULL) {
+						log_error("Unable to allocate memory: %s\n", strerror(errno));
+						free(work);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					memset(sin6, 0, sizeof(struct sockaddr_in6));
+					sin6->sin6_family = AF_INET6;
+					if (inet_pton(AF_INET6, work, &sin6->sin6_addr) != 1) {
+						log_error("'%s' is not a valid IPv6 address: %s\n", work, strerror(errno));
+						free(work);
+						free(sin6);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					sin6->sin6_port = htons(p);
+					yylval->ss = (struct sockaddr *)sin6;
+				} else {
+					struct sockaddr_in * sin = NULL;
+				
+					sin = malloc(sizeof(struct sockaddr_in));
+					if (sin == NULL) {
+						log_error("Unable to allocate memory: %s\n", strerror(errno));
+						free(work);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					memset(sin, 0, sizeof(struct sockaddr_in));
+					sin->sin_family = AF_INET;
+					if (inet_pton(AF_INET, work, &sin->sin_addr) != 1) {
+						log_error("'%s' is not a valid IP address: %s\n", work, strerror(errno));
+						free(work);
+						free(sin);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					sin->sin_port = htons(p);
+					yylval->ss = (struct sockaddr *)sin;
+				}
+				free(work);
+				return IP;
+			}
+
+
+<IN_CLI1>"/"		{ BEGIN(IN_CLI2); return '/';	 		}
+
+
+	/* Servers section */
+(?i:"auth_server_enable")	{ return AUTH_ENABLE; 				}
+(?i:"auth_server_port")		{ BEGIN(EXPECT_DECINT); return AUTH_PORT; 	}
+(?i:"auth_server_ip4")		{ BEGIN(EXPECT_IP4); return AUTH_IP4; 		}
+(?i:"auth_server_ip6")		{ BEGIN(EXPECT_IP6); return AUTH_IP6; 		}
+(?i:"acct_server_enable")	{ return ACCT_ENABLE; 				}
+(?i:"acct_server_port")		{ BEGIN(EXPECT_DECINT); return ACCT_PORT; 	}
+(?i:"acct_server_ip4")		{ BEGIN(EXPECT_IP4); return ACCT_IP4; 		}
+(?i:"acct_server_ip6")		{ BEGIN(EXPECT_IP6); return ACCT_IP6; 		}
+
+<EXPECT_DECINT>[[:digit:]]+	{
+					/* Match an integer (not hexa) */
+					int ret = sscanf(yytext, "%d", &yylval->integer);
+					if (ret != 1) {
+						/* No matching: an error occurred */
+						log_error("Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno));
+						return LEX_ERROR; /* trig an error in yacc parser */
+						/* Maybe we could REJECT instead of failing here? */
+					}
+					return INTEGER;
+				}
+
+<EXPECT_IP4,EXPECT_IP6>(?i:"disable")	{ return DISABLED; 				}
+				
+<EXPECT_IP4>{IP4}		{
+					struct sockaddr_in * sin = NULL;
+				
+					sin = malloc(sizeof(struct sockaddr_in));
+					if (sin == NULL) {
+						log_error("Unable to allocate memory: %s\n", strerror(errno));
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					memset(sin, 0, sizeof(struct sockaddr_in));
+					sin->sin_family = AF_INET;
+					if (inet_pton(AF_INET, yytext, &sin->sin_addr) != 1) {
+						log_error("'%s' is not a valid IP address: %s\n", yytext, strerror(errno));
+						free(sin);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					yylval->ss = (struct sockaddr *)sin;
+					return IP;
+				}
+
+<EXPECT_IP6>{IP6}		{
+					struct sockaddr_in6 * sin6 = NULL;
+				
+					sin6 = malloc(sizeof(struct sockaddr_in6));
+					if (sin6 == NULL) {
+						log_error("Unable to allocate memory: %s\n", strerror(errno));
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					
+					memset(sin6, 0, sizeof(struct sockaddr_in6));
+					sin6->sin6_family = AF_INET6;
+					if (inet_pton(AF_INET6, yytext, &sin6->sin6_addr) != 1) {
+						log_error("'%s' is not a valid IPv6 address: %s\n", yytext, strerror(errno));
+						free(sin6);
+						return LEX_ERROR; /* trig an error in yacc parser */
+					}
+					yylval->ss = (struct sockaddr *)sin6;
+					return IP;
+				}
+
+	
+	/* Valid single characters for yyparse in all contexts */
+<*>[=]			{ return yytext[0]; }
+<*>[;]			{ BEGIN(INITIAL); return yytext[0]; }
+
+	/* Unrecognized token */
+<*>[[:alnum:]]+		|	/* This rule is only useful to print a complete token in error messages */
+	/* Unrecognized character */
+<*>.			{ 
+				log_error("Unrecognized text on line %d col %d: '%s'.\n", yylloc->first_line, yylloc->first_column, yytext);
+			 	return LEX_ERROR; 
+			}
+
+%%
--- a/extensions/radius_gw/radius_gw.y	Thu May 07 11:01:46 2009 +0900
+++ b/extensions/radius_gw/radius_gw.y	Fri May 08 18:28:41 2009 +0900
@@ -32,3 +32,431 @@
 * 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/radius_gw.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
+
+/* 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... */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+/* Forward declaration */
+int yyparse(char * conffile);
+
+/* Declare the rgw_servers */
+struct rgw_servs rgw_servers;
+
+/* 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;
+		log_error("Unable to open extension configuration file %s for reading: %s\n", conffile, strerror(ret));
+		return ret;
+	}
+
+	ret = radius_gwparse(conffile);
+
+	fclose(rgw_confin);
+
+	if (ret != 0) {
+		return EINVAL;
+	}
+	
+	return 0;
+}
+
+void rgw_dump_conf(void)
+{
+	char ipstr[INET6_ADDRSTRLEN];
+	
+	log_normal("-------------------------\n");
+	
+	log_normal(" auth server:\n");
+	log_normal("    disabled..... : %s\n", rgw_servers.auth_serv.disabled ? "TRUE":"false");
+	log_normal("    IP disabled.. : %s\n", rgw_servers.auth_serv.ipv4_disabled ? "TRUE":"false");
+	log_normal("    IPv6 disabled : %s\n", rgw_servers.auth_serv.ipv6_disabled ? "TRUE":"false");
+	log_normal("    port......... : %hu\n", ntohs(rgw_servers.auth_serv.port));
+	inet_ntop(AF_INET, &rgw_servers.auth_serv.ipv4_endpoint,ipstr,sizeof(ipstr));
+	log_normal("    IP bind...... : %s\n", ipstr);
+	inet_ntop(AF_INET6, &rgw_servers.auth_serv.ipv6_endpoint,ipstr,sizeof(ipstr));
+	log_normal("    IPv6 bind.... : %s\n", ipstr);
+
+	log_normal(" acct server:\n");
+	log_normal("    disabled..... : %s\n", rgw_servers.acct_serv.disabled ? "TRUE":"false");
+	log_normal("    IP disabled.. : %s\n", rgw_servers.acct_serv.ipv4_disabled ? "TRUE":"false");
+	log_normal("    IPv6 disabled : %s\n", rgw_servers.acct_serv.ipv6_disabled ? "TRUE":"false");
+	log_normal("    port......... : %hu\n", ntohs(rgw_servers.acct_serv.port));
+	inet_ntop(AF_INET, &rgw_servers.acct_serv.ipv4_endpoint,ipstr,sizeof(ipstr));
+	log_normal("    IP bind...... : %s\n", ipstr);
+	inet_ntop(AF_INET6, &rgw_servers.acct_serv.ipv6_endpoint,ipstr,sizeof(ipstr));
+	log_normal("    IPv6 bind.... : %s\n", ipstr);
+
+	log_normal("-------------------------\n");
+}
+
+void rgw_conf_init(void)
+{
+	memset(&rgw_servers, 0, sizeof(rgw_servers));
+
+	rgw_servers.auth_serv.port = htons(1812);
+	rgw_servers.auth_serv.ipv4_endpoint.s_addr = INADDR_ANY;
+	memcpy(&rgw_servers.auth_serv.ipv6_endpoint, &in6addr_any, sizeof(struct in6_addr));
+	
+	rgw_servers.acct_serv.port = htons(1813);
+	rgw_servers.acct_serv.ipv4_endpoint.s_addr = INADDR_ANY;
+	memcpy(&rgw_servers.acct_serv.ipv6_endpoint, &in6addr_any, sizeof(struct in6_addr));
+}
+
+
+/* The Lex parser prototype */
+int radius_gwlex(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)
+		log_error("%s:%d.%d-%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
+	else if (ploc->first_column != ploc->last_column)
+		log_error("%s:%d.%d-%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
+	else
+		log_error("%s:%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, s);
+}
+
+/* This function checks a string value is a valid filename */
+static int is_valid_file( char * candidate ) 
+{
+	int ret;
+	struct stat buffer;
+	
+	ret = stat(candidate, &buffer);
+	if (ret != 0) {
+		log_error("Error on file '%s': %s.\n", candidate, strerror(errno));
+		return 0;
+	}
+
+	/* Ok this candidate is valid */
+	return 1;
+}
+
+/* Very simple byte stack management */
+static unsigned char * buf = NULL;
+static size_t buf_sz = 0;
+static size_t buf_rsz = 0;
+static inline int buf_add(unsigned char val) /* add a value in the array */
+{
+	buf_sz++;
+	
+	if (buf_sz > buf_rsz) {
+		void * rez=NULL;
+		buf_rsz += 256;
+		rez = realloc(buf, buf_rsz);
+		if (rez == NULL) {
+			log_error("Error on memory allocation: %s\n", strerror(errno));
+			return 0;
+		}
+		buf = (unsigned char *)rez;
+	}
+	buf[buf_sz - 1] = val;
+	return 1;   
+}
+static inline void buf_reinit(void)
+{
+	free(buf);
+	buf = NULL;
+	buf_sz = 0;
+	buf_rsz = 0;
+}
+	
+
+%}
+
+/* Values returned by lex for token */
+%union {
+	char 		*string;	/* The string is allocated by strdup in lex.*/
+	int		 integer;	/* Store integer values */
+	struct sockaddr *ss;		/* sockaddr to free after use (alloc in lex) */
+}
+
+/* typed data */
+%token <string>	QSTRING
+%token <integer> INTEGER
+%token <ss>	IP
+
+%type <string>	FILENAME
+%type <string>	extconf
+%type <integer> extport
+
+/* simple tokens */
+%token		DISABLED
+%token		AUTH
+%token		ACCT
+
+%token 		EXT_PREFIX
+%token 		CLI_PREFIX
+
+%token		AUTH_ENABLE
+%token		AUTH_PORT
+%token		AUTH_IP4
+%token		AUTH_IP6
+%token		ACCT_ENABLE
+%token		ACCT_PORT
+%token		ACCT_IP4
+%token		ACCT_IP6
+
+/* In case of error in the lexical analysis */
+%token 		LEX_ERROR
+
+
+/* -------------------------------------- */
+%%
+
+	/* The grammar definition */
+conffile:		/* empty grammar is OK */
+			| conffile extension
+			| conffile clientdef
+			| conffile authserv
+			| conffile acctserv
+			;
+
+				
+/* -------------------------------------- */
+FILENAME:		QSTRING
+			{
+				/* Verify this is a valid file */
+				if (!is_valid_file($1)) {
+					yyerror (&yylloc, conffile, "Error on file name, aborting...");
+					YYERROR;
+				}
+				$$ = $1;
+			}
+			;
+/* -------------------------------------- */
+
+extension:		{
+				buf_reinit();
+			}
+			EXT_PREFIX '=' FILENAME extconf extport extcodes ';'
+			{
+				/* Add this extension in the list */
+				if ( rgw_add_extension( $4, $5, $6, buf, buf_sz ) ) {
+					yyerror (&yylloc, conffile, "Error parsing / adding extension !");
+					YYERROR;
+				}
+				
+				/* Free the array */
+				buf_reinit();
+			}
+			;
+			
+extconf:		/* empty */
+			{ 
+				$$ = NULL;
+			}
+			| ':' QSTRING
+			{
+				$$ = $2;
+			}
+			;
+			
+extport:		/* empty */
+			{
+				$$ = RGW_EXT_PORT_AUTH | RGW_EXT_PORT_ACCT ;
+			}
+			| ':' AUTH
+			{
+				$$ = RGW_EXT_PORT_AUTH;
+			}
+			| ':' ACCT
+			{
+				$$ = RGW_EXT_PORT_ACCT;
+			}
+
+extcodes:		/* empty */
+			| ':' extcodes_list
+			;
+			
+extcodes_list:		/* empty */
+			| extcodes_list INTEGER
+			{
+				if ($2 < 0 || $2 > 255) {
+					yyerror (&yylloc, conffile, "Invalid command code value!");
+					YYERROR;
+				}
+				if ( ! buf_add((unsigned char)$2) ) {
+					yyerror (&yylloc, conffile, "Error allocating memory!");
+					YYERROR;
+				}
+			}
+			;
+				
+/* -------------------------------------- */
+
+clientdef:		{
+				buf_reinit();
+			}
+			CLI_PREFIX '=' IP '/' clisecret_key ';'
+			{
+				/* Add this client */
+				if ( rgw_add_client( $4, buf, buf_sz ) ) {
+					yyerror (&yylloc, conffile, "Error parsing / adding client !");
+					YYERROR;
+				}
+				
+				/* reinit the buffer */
+				buf_reinit();
+			}
+			;
+
+clisecret_key:		/* empty */
+			| clisecret_key QSTRING
+			{
+				int i;
+				size_t len = strlen($2);
+				for (i = 0; i < len; i++) {
+					if ( ! buf_add( $2 [i] ) ) {
+						yyerror (&yylloc, conffile, "Memory allocation error.");
+						YYERROR;
+					}
+				}
+				
+				free($2);
+			}
+			| clisecret_key INTEGER
+			{
+				if ( $2 < 0 || $2 > 255 ) {
+					yyerror (&yylloc, conffile, "Invalid value in key.");
+					YYERROR;
+				}
+				
+				if ( ! buf_add( $2 ) ) {
+					yyerror (&yylloc, conffile, "Memory allocation error.");
+					YYERROR;
+				}
+			}
+			;
+
+/* -------------------------------------- */
+
+authserv:		AUTH_ENABLE '=' INTEGER ';'
+			{
+				if ($3 == 0) {
+					rgw_servers.auth_serv.disabled = 1;
+				} else {
+					rgw_servers.auth_serv.disabled = 0;
+				}
+			}
+			| AUTH_PORT '=' INTEGER ';'
+			{
+				if ($3 <= 0 || $3 > 65535) {
+					yyerror (&yylloc, conffile, "Invalid port number !");
+					YYERROR;
+				}
+					
+				rgw_servers.auth_serv.port = htons($3);
+			}
+			| AUTH_IP4 '=' DISABLED ';'
+			{
+				rgw_servers.auth_serv.ipv4_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.ipv4_endpoint, &((struct sockaddr_in *)($3))->sin_addr, sizeof(struct in_addr) );
+				free($3);
+				rgw_servers.auth_serv.ipv4_disabled = 0;
+			}
+			| AUTH_IP6 '=' DISABLED ';'
+			{
+				rgw_servers.auth_serv.ipv6_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.ipv6_endpoint, &((struct sockaddr_in6 *)($3))->sin6_addr, sizeof(struct in6_addr) );
+				free($3);
+				rgw_servers.auth_serv.ipv6_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.ipv4_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.ipv4_endpoint, &((struct sockaddr_in *)($3))->sin_addr, sizeof(struct in_addr) );
+				free($3);
+				rgw_servers.acct_serv.ipv4_disabled = 0;
+			}
+			| ACCT_IP6 '=' DISABLED ';'
+			{
+				rgw_servers.acct_serv.ipv6_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.ipv6_endpoint, &((struct sockaddr_in6 *)($3))->sin6_addr, sizeof(struct in6_addr) );
+				free($3);
+				rgw_servers.acct_serv.ipv6_disabled = 0;
+			}
+			;
--- a/extensions/radius_gw/radius_gw_internal.h	Thu May 07 11:01:46 2009 +0900
+++ b/extensions/radius_gw/radius_gw_internal.h	Fri May 08 18:28:41 2009 +0900
@@ -44,9 +44,11 @@
 /* The content of this file is mainly used to declare interfaces 
     between lex/yacc files and the main extension file. */
 
-int rgw_add_extension( /* file, conffile, port(s), code_array, code_len */ );
+#define RGW_EXT_PORT_AUTH	1
+#define RGW_EXT_PORT_ACCT	2
+int rgw_add_extension( char * extfile, char * conffile, int port, unsigned char * codes_array, size_t codes_sz );
 
-int rgw_add_client(/* ip, key, keylen */);
+int rgw_add_client( struct sockaddr * ip_port, unsigned char * key, size_t keylen );
 
 struct rgw_serv {
 	unsigned	disabled	:1;
@@ -56,15 +58,21 @@
 	
 	uint16_t	port;	/* stored in network byte order */
 	
-	struct sockaddr_in	ipv4_endpoint;
-	struct sockaddr_in6	ipv6_endpoint;
+	struct in_addr	ipv4_endpoint;
+	struct in6_addr	ipv6_endpoint;
 };
 
-struct {
+extern struct rgw_servs {
 	struct rgw_serv	auth_serv;
 	struct rgw_serv	acct_serv;
 } rgw_servers;
 
+void rgw_conf_init(void);
+int rgw_conf_handle(char * conffile);
+
+void rgw_dump_conf(void);
+void rgw_dump_extensions(void);
+void rgw_dump_clients(void);
 
 #endif /* _RADIUS_GW_INTERNAL_H */
   
--- a/extensions/radius_gw/radius_msg.c	Thu May 07 11:01:46 2009 +0900
+++ b/extensions/radius_gw/radius_msg.c	Fri May 08 18:28:41 2009 +0900
@@ -41,6 +41,7 @@
 modules do not need to "know" the actual representation of RADIUS messages on the network. They only 
 receive the logical view as exposed in the radius_gw.h file. */
 
+#include "radius_gw_internal.h"
 
 /* To ensure packed structures with some common compilers */
 #ifdef __GNUC__
@@ -53,22 +54,22 @@
 #endif /* _MSC_VER */
 
 struct radius_hdr {
-	u8 code;
-	u8 identifier;
-	u16 length; /* including this header */
-	u8 authenticator[16];
+	uint8_t code;
+	uint8_t identifier;
+	uint16_t length; /* including this header */
+	uint8_t authenticator[16];
 	/* followed by length-20 octets of attributes */
 } STRUCT_PACKED;
 
 struct radius_attr_hdr {
-	u8 type;
-	u8 length; /* including this header */
+	uint8_t type;
+	uint8_t length; /* including this header */
 	/* followed by length-2 octets of attribute value */
 } STRUCT_PACKED;
 
 struct radius_attr_vendor {
-	u8 vendor_type;
-	u8 vendor_length;
+	uint8_t vendor_type;
+	uint8_t vendor_length;
 } STRUCT_PACKED;
 
 #ifdef _MSC_VER
"Welcome to our mercurial repository"