view extensions/radius_gw/sub_echo_drop.c @ 374:883330e610e1

Progress on the echo_drop sub extension
author Sebastien Decugis <sdecugis@nict.go.jp>
date Tue, 26 May 2009 11:37:32 +0900
parents 0cb02e490017
children 98806d0e175f
line wrap: on
line source

/*********************************************************************************************************
* Software License Agreement (BSD License)                                                               *
* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
*													 *
* Copyright (c) 2009, WIDE Project and NICT								 *
* All rights reserved.											 *
* 													 *
* Redistribution and use of this software in source and binary forms, with or without modification, are  *
* permitted provided that the following conditions are met:						 *
* 													 *
* * Redistributions of source code must retain the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer.										 *
*    													 *
* * Redistributions in binary form must reproduce the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer in the documentation and/or other						 *
*   materials provided with the distribution.								 *
* 													 *
* * Neither the name of the WIDE Project or NICT nor the 						 *
*   names of its contributors may be used to endorse or 						 *
*   promote products derived from this software without 						 *
*   specific prior written permission of WIDE Project and 						 *
*   NICT.												 *
* 													 *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
*********************************************************************************************************/

/* See sub_echo_drop.h for details */

#define DECLARE_API_POINTERS
#include "sub_echo_drop.h"

int sub_echo_drop_verbosity = SUB_ECHO_DROP_VERBO;

/* Create the configuration and state structure */
static struct rga_conf_state * sed_cs_create(char * conffile)
{
	struct rga_conf_state * cs = NULL;
	
	TRACE_ENTRY("%p", conffile);
	
	if ( ! conffile ) {
		log_error("No configuration file specified for extension sub_echo_drop!\n");
		return NULL;
	}
	
	/* Create a new storage for extension state */
	CHECK_MALLOC_DO( cs = malloc(sizeof(struct rga_conf_state)), return NULL );
	memset(cs, 0, sizeof(struct rga_conf_state));
	
	rg_list_init(&cs->conf);
	
	/* Initialize the session handler, to store "echo" attributes */
	CHECK_FCT_DO( sess_regext( &cs->sess_hdl ), { free(cs); return NULL; } );
	
	/* Parse the configuration file with the yacc parser */
	cs->conffile = conffile;
	CHECK_FCT_DO( sed_conf_parse(cs), { sess_deregext(cs->sess_hdl); free(cs); return NULL; } );
	
	TRACE_DEBUG(INFO, "Extension Echo/Drop initialized with configuration: '%s'", cs->conffile);	
	if (TRACE_BOOL(FULL)) {
		struct rg_list * li;
		
		for (li = cs->conf.next; li != &cs->conf; li = li->next) {
			struct sed_conf_item * sci = (struct sed_conf_item *)li;
			char * act = (sci->action == ACT_ECHO) ? "ECHO" : "DROP";
			if (sci->ext) {
				log_debug("  %s Code: %hhu, Vendor: %u, Ext-Type: %hu\n", act, sci->code, sci->vendor_id, sci->extype);
				continue;
			}
			if (sci->tlv) {
				log_debug("  %s Code: %hhu, Vendor: %u, Type: %hhu\n", act, sci->code, sci->vendor_id, sci->type);
				continue;
			}
			if (sci->vsa) {
				log_debug("  %s Code: %hhu, Vendor: %u\n", act, sci->code, sci->vendor_id);
				continue;
			}
			log_debug("  %s Code: %hhu\n", act, sci->code);
		}
	}
	
	/* We're done */
	return cs;
}

/* Destroy the state */
static void sed_cs_destroy(struct rga_conf_state * cs)
{
	
	TRACE_ENTRY("%p", cs);
	CHECK_PARAMS_DO( cs, );
	
	while (! rg_list_is_empty(&cs->conf) ) {
		struct rg_list * li = cs->conf.next;
		rg_list_unlink(li);
		free(li);
	}
	
	CHECK_FCT_DO( sess_deregext( cs->sess_hdl ),  );
	
	free(cs);
	return;
}

/* Destroy a list of attributes saved in a session */
static void ssi_cleanup(void * ptr)
{
	TRACE_ENTRY("%p", ptr);
	CHECK_PARAMS_DO( ptr , return );
	
	struct rg_list *list = (struct rg_list *)ptr;
	while (! rg_list_is_empty(list) ) {
		struct sed_saved_item * ssi = (struct sed_saved_item *)(list->next);
		rg_list_unlink(&ssi->chain);
		free(ssi);
	}
	free(list);
}

/* Handle attributes from a RADIUS request as specified in the configuration */
static int sed_rad_req(struct rga_conf_state * cs, sess_id_t ** session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, msg_t ** diam_fw )
{
	size_t *nattr_pos;
	size_t nattr_used = 0;
	int idx;
	
	struct rg_list echo_list;
	struct rg_list *li;
	
	TRACE_ENTRY("%p %p %p %p %p", cs, session, rad_req, rad_ans, diam_fw);
	CHECK_PARAMS(cs && rad_req);
	
	rg_list_init(&echo_list);
	
	CHECK_MALLOC( nattr_pos = malloc(rad_req->attr_size * sizeof(size_t)) );
	
	/* 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->conf.next; li != &cs->conf; li = li->next) {
			struct sed_conf_item * sci = (struct sed_conf_item *)li;
			uint32_t vid;
			unsigned char * ptr;
			
			if (sci->code < attr->type)
				continue;
			if (sci->code > attr->type)
				break;
			
			/* matching code */
			
			if (! sci->vsa) {
				action = sci->action;
				break;
			}
			
			if (attr->length < 8)
				continue;
			
			ptr = (unsigned char *)(attr + 1);
			/* since attr is not aligned, we cannot access *(attr+1) directly */
			memcpy(&vid, ptr, sizeof(uint32_t));
			
			if (sci->vendor_id < ntohl(vid))
				continue;
			if (sci->vendor_id > ntohl(vid))
				break;
			
			/* Matching vendor */
			
			if ( ! sci->tlv && ! sci->ext ) {
				action = sci->action;
				break;
			}
			
			if (attr->length < 10)
				continue;
			
			if (sci->tlv) {
				struct radius_attr_vendor * tl = (struct radius_attr_vendor *)(ptr + sizeof(uint32_t));
				if (tl->vendor_type == sci->type) {
					action = sci->action;
					break;
				}
				continue;
			}
			
			if (sci->ext) {
				/* To be done */
				ASSERT(0);
			}
		}
		
		switch (action) {
			case ACT_DROP:
				TRACE_DEBUG(FULL, "Dropping attribute with code %hhd", attr->type);
				break;
				
			case ACT_ECHO:
				{
					struct sed_saved_item * sav = NULL;
					TRACE_DEBUG(FULL, "Saving attribute with code %hhd", attr->type);
					CHECK_MALLOC( sav = malloc(sizeof(struct sed_saved_item) + attr->length - sizeof(struct radius_attr_hdr)) );
					rg_list_init(&sav->chain);
					memcpy(&sav->attr, attr, attr->length);
					rg_list_insert_before(&echo_list, &sav->chain);
				}
				break;
			
			case 0: /* Attribute was not specified in the configuration */
			default: /* unknown action value */
				/* We just keep the attribute in the RADIUS message */
				nattr_pos[nattr_used++] = rad_req->attr_pos[idx];
		}
	}
	
	/* Save the echoed values in the session, if any */
	if (!rg_list_is_empty(&echo_list)) {
		CHECK_PARAMS(session && *session);
		CHECK_MALLOC( li = malloc(sizeof(struct rg_list)) );
		memcpy(li, &echo_list, sizeof(struct rg_list));
		
		/* Check that we have no previous data stored for this session, for debug */
		ASSERT( sess_data_dereg(*session, cs->sess_hdl, NULL) == ENOENT );
		
		/* Save the list in the session */
		CHECK_FCT( sess_data_reg(*session, cs->sess_hdl, li, ssi_cleanup) );
	}
	
	/* Finally update the radius message to remove all handled attributes */
	free(rad_req->attr_pos);
	rad_req->attr_pos = nattr_pos;
	rad_req->attr_used = nattr_used;
	
	return 0;
}

/* Process an answer: add back the ECHO attributes, if any */
static int sed_diam_ans(struct rga_conf_state * cs, sess_id_t ** session, msg_t ** diam_ans, struct radius_msg ** rad_fw )
{
	int ret;
	struct rg_list * list = NULL;
	
	TRACE_ENTRY("%p %p %p %p", cs, session, diam_ans, rad_fw);
	CHECK_PARAMS(cs);
	
	/* If there is no session associated, just give up */
	if (! session || ! *session) {
		TRACE_DEBUG(FULL, "No session associated with the message, nothing to do here...");
		return 0;
	}
	
	/* No try and retrieve any data from the session */
	ret = sess_data_dereg( *session, cs->sess_hdl, (void *)&list );
	if (ret == ENOENT) {
		TRACE_DEBUG(FULL, "No data saved in the session, no attribute to add back");
		return 0;
	}
	CHECK_FCT(ret); /* Return if another error occurred */
	
	/* From this point on, we have a list of attributes to add to the radius message */
	
	CHECK_PARAMS( rad_fw );
	if ( *rad_fw == NULL ) {
		/* Will implement this when main extension is complete */
		ASSERT(0);
		return ENOTSUP;
	}
	
	while (! rg_list_is_empty(list) ) {
		struct sed_saved_item * ssi = (struct sed_saved_item *)(list->next);
		struct radius_attr_hdr * rc;
		
		rg_list_unlink(&ssi->chain);
		
		TRACE_DEBUG(FULL, "Echo attribute in the RADIUS answer: type %hhu, len: %hhu", ssi->attr.type, ssi->attr.length);
		
		/* Add this attribute in the RADIUS message */
		CHECK_MALLOC( radius_msg_add_attr(*rad_fw, ssi->attr.type, (unsigned char *)(ssi + 1), ssi->attr.length - sizeof(struct radius_attr_hdr)) );
		
		free(ssi);
	}
	free(list);

	return 0;
}



int rga_register(int version, waaad_api_t * waaad_api, struct radius_gw_api * api)
{
	TRACE_ENTRY("%d %p %p", version, waaad_api, 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;
	}
	
	/* Required to use the waaad api from this sub-extension: */
	EXTENSION_API_INIT_INTERN( API_MODULE_ALL, "sub_echo_drop", waaad_api );
	
	/* Initialize the radius_gw api callbacks */
	api->rga_conf_parse_cb = sed_cs_create;
	api->rga_conf_free_cb  = sed_cs_destroy;
	api->rga_rad_req_cb    = sed_rad_req;
	api->rga_diam_ans_cb   = sed_diam_ans;
	
	/* We're done, we must not initialize any state here since the extension must be re-entrant, but in sample_conf_parse */
	return 0;
}
"Welcome to our mercurial repository"