Initial commit

Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
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
+};