Mercurial > hg > waaad
view extensions/radius_gw/rgw_work.c @ 415:540ed390c04f
Added sess_destroy function
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Tue, 16 Jun 2009 13:37:46 +0900 |
parents | 0bd2a40ca008 |
children | 4d56917a5e42 |
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. * *********************************************************************************************************/ /* Manage incoming RADIUS message. */ #include "radius_gw.h" /* How many threads to work on messages ? */ #define NB_WORKERS 2 static pthread_mutex_t work_mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER; static sess_reg_t * sess_hdl = NULL; static pthread_t workers[NB_WORKERS]; static struct rg_list work_data; /* List of pending messages to be handled */ struct work_item { struct rg_list chain; struct rgw_radius_msg_meta * msg; struct rgw_client * cli; }; /* Structure stored in waaad when waiting for a Diameter answer */ struct pending_answer { struct rgw_radius_msg_meta * rad; struct rgw_client * cli; sess_id_t * sess; }; /* Callback when a Diameter answer is received */ static void work_receive_diam_answer(void * paback, msg_t **ans) { struct pending_answer * pa = (struct pending_answer *)paback; TRACE_ENTRY("%p %p", pa, ans); CHECK_PARAMS_DO( pa && ans, return ); TRACE_DEBUG(INFO, "Handling Diameter answer: Not implemented yet..."); /* Create an empty RADIUS answer message */ /* Pass the Diameter answer to the same extensions as the request */ /* Now try and send the RADIUS answer */ out: /* Destroy remaining session data (stateless gateway) */ CHECK_FCT_DO( sess_destroy(&pa->sess), ); /* Clear the Diameter message */ CHECK_FCT_DO( msg_free(ans, 1), ); /* Clear the answer data */ free(pa); /* Finished */ return; } /* Worker thread, processing incoming RADIUS messages (after parsing) */ static void * work_th(void * arg) { char thname[10]; TRACE_ENTRY("%p", arg); snprintf(thname, sizeof(thname), "worker #%d", (int)arg); THREAD_NAME(thname); while (1) { /* The thread will be cancelled */ struct rgw_radius_msg_meta * msg; struct rgw_client * cli; struct work_item * wi; sess_id_t * session; msg_t * diam_msg; int pb, a; struct pending_answer * pa; /* Wait for the next incoming RADIUS message */ CHECK_POSIX_DO( pthread_mutex_lock(&work_mtx), break ); pthread_cleanup_push(rg_cleanup_mutex, &work_mtx); while (rg_list_is_empty(&work_data)) { CHECK_POSIX_DO( pthread_cond_wait( &work_cond, &work_mtx ), { pthread_mutex_unlock(&work_mtx); return NULL; } ); } wi = (struct work_item *)(work_data.next); rg_list_unlink(&wi->chain); msg = wi->msg; cli = wi->cli; free(wi); pthread_cleanup_pop(0); CHECK_POSIX_DO( pthread_mutex_unlock(&work_mtx), break ); TRACE_DEBUG(ANNOYING, "Processing next RADIUS message: %p received on client: %p", msg, cli); /* process the data */ /* Check authenticator, if any */ CHECK_FCT_DO( rgw_msg_auth_check(msg, cli, NULL), { /* An error occurred, discard message */ rgw_msg_free(&msg); rgw_clients_dispose(&cli); continue; } ); /* Check duplicate */ CHECK_FCT_DO( rgw_clients_check_dup(&msg, cli), { /* An error occurred, discard message */ rgw_msg_free(&msg); rgw_clients_dispose(&cli); continue; } ); if (msg == NULL) { rgw_clients_dispose(&cli); continue; /* the message was a duplicate */ } /* Check that IP is coherent with the identity in the message */ CHECK_FCT_DO( rgw_clients_check_origin(msg, cli), { /* An error occurred, discard message */ rgw_msg_free(&msg); rgw_clients_dispose(&cli); continue; } ); /* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore! */ session = NULL; diam_msg = NULL; /* Create the session and an empty message with some common AVPs */ CHECK_FCT_DO( rgw_msg_create_base(msg, cli, &session, &diam_msg), { /* An error occurred, discard message */ rgw_msg_free(&msg); rgw_clients_dispose(&cli); continue; } ); /* Pass the message to the list of registered extensions */ CHECK_FCT_DO( rgw_extensions_loop_req(&msg, &session, &diam_msg, cli), { /* An error occurred, discard message */ rgw_msg_free(&msg); rgw_clients_dispose(&cli); continue; } ); if (msg == NULL) { rgw_clients_dispose(&cli); continue; /* the message was handled already */ } pb = 0; /* Check the created Diameter message */ if ((diam_msg == NULL) || ( msg_parse_rules(diam_msg, NULL) ) ) { log_error("No or invalid Diameter generated after processing of RADIUS command %hhd (%s).\n" " Turn on advanced log for detail.\n", msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code)); /* We might also dump the conflicting rule here if useful */ pb++; } /* Check if the full content of the RADIUS message was handled */ for (a = 0; a < msg->radius.attr_used; a++) { struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[a]); pb++; log_error("No extension available to handle attribute %hhd (%s) in command %hhd (%s)!\n", attr->type, rgw_msg_attrtype_str(attr->type), msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code)); } /* Check the session is correct */ if (session == NULL) { log_error("No session has been created to store RADIUS state (command %hhd (%s)).\n" " Please check your configuration and documentation.\n", msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code)); pb++; } if (pb) { /* Something went wrong during the conversion */ if (session) { CHECK_FCT_DO( sess_destroy(&session), ); } if (diam_msg) { CHECK_FCT_DO( msg_free(diam_msg, 1), ); diam_msg = NULL; } rgw_msg_free(&msg); rgw_clients_dispose(&cli); TRACE_DEBUG(INFO, "%d problem(s) occurred while translating a RADIUS message, data discarded.\n", pb); continue; } /* Send the radius message and register for answer */ CHECK_MALLOC_DO( pa = malloc(sizeof(struct pending_answer)), break ); memset(pa, 0, sizeof(*pa)); pa->rad = msg; pa->cli = cli; pa->sess= session; CHECK_FCT_DO( msg_send( &diam_msg, work_receive_diam_answer, pa), { /* If an error occurs, log and destroy the data */ log_error("An error occurred while sending Diameter message, please turn Debug on for detail.\n"); if (session) { CHECK_FCT_DO( sess_destroy(&session), ); } if (diam_msg) { CHECK_FCT_DO( msg_free(diam_msg, 1), ); diam_msg = NULL; } rgw_msg_free(&msg); rgw_clients_dispose(&cli); free(pa); continue; } ); /* Done! */ } TRACE_DEBUG(INFO, "Error: thread terminated!"); return NULL; } int rgw_work_start(void) { int i; TRACE_ENTRY(); CHECK_FCT( sess_regext(&sess_hdl) ); memset(workers, 0, sizeof(workers)); rg_list_init(&work_data); /* Create the worker thread(s) */ for (i = 0; i < NB_WORKERS; i++) { CHECK_POSIX( pthread_create(&workers[i], NULL, work_th, (void *)i) ); } return 0; } int rgw_work_add(struct rgw_radius_msg_meta * msg, struct rgw_client * client) { struct work_item * new; CHECK_MALLOC( new = malloc(sizeof(struct work_item)) ); memset(new, 0, sizeof(struct work_item)); rg_list_init(&new->chain); new->msg = msg; new->cli = client; CHECK_POSIX( pthread_mutex_lock(&work_mtx) ); rg_list_insert_before(&work_data, &new->chain); CHECK_POSIX( pthread_cond_signal(&work_cond) ); CHECK_POSIX( pthread_mutex_unlock(&work_mtx) ); return 0; } void rgw_work_fini(void) { int i; TRACE_ENTRY(); for (i = 0; i < NB_WORKERS; i++) { rg_thread_term(&workers[i]); } CHECK_FCT_DO( sess_deregext(sess_hdl), ); return; }