Commit 542b5148 authored by Sebastien Decugis's avatar Sebastien Decugis
Browse files

Added dispatch module and tests

parent fdafd9fe
......@@ -16,6 +16,7 @@ SET(TEST_LIST
testmesg
testmq
testsess
testdisp
)
#############################
......
This diff is collapsed.
......@@ -110,4 +110,35 @@ int fd_msg_rescode_set( struct msg * msg, struct dictionary * dict, char * resco
int fd_msg_add_origin ( struct msg * msg, struct dictionary * dict, int osi ); /* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */
/***************************************/
/* Dispatch module, daemon's part */
/***************************************/
enum {
DISP_APP_AUTH = 1,
DISP_APP_ACCT = 2
};
/*
* FUNCTION: fd_disp_app_support
*
* PARAMETERS:
* app : The dictionary object corresponding to the Application.
* vendor : (Optional) the dictionary object of a Vendor to claim support in Vendor-Specific-Application-Id
* flags : Combination of DISP_APP_* flags.
*
* DESCRIPTION:
* Registers an application to be advertized in CER/CEA exchanges.
* Messages with an application-id matching a registered value are passed to the dispatch module,
* while other messages are simply relayed or an error is returned (if local node does not relay)
*
* RETURN VALUE:
* 0 : The application support is registered.
* EINVAL : A parameter is invalid.
*/
int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int flags );
/* Note: if we want to support capabilities updates, we'll have to add possibility to remove an app as well... */
#endif /* _FREEDIAMETER_H */
......@@ -1506,124 +1506,6 @@ void fd_sess_dump(int level, struct session * session);
void fd_sess_dump_hdl(int level, struct session_handler * handler);
/*============================================================*/
/* DISPATCH */
/*============================================================*/
/* Dispatch module (passing incoming messages to extensions registered callbacks)
* is split between the library and the daemon.
*
* The library provides the support for associating dispatch callbacks with
* dictionary objects.
*
* The daemon is responsible for calling the callbacks for a message when appropriate.
*
*
* The dispatch module has two main roles:
* - help determine if a message can be handled locally (during the routing step)
* This decision involves only the application-id of the message.
* - pass the message to the callback(s) that will handle it (during the dispatch step)
*
* These are the possibilities for registering a so-called dispatch callback:
*
* -> For All messages.
* This callback is called for all messages that are handled locally. This should be used only
* for debug purpose.
*
* -> by AVP value (constants only).
* This callback will be called when a message is received and contains an AVP with a specified enumerated value.
*
* -> by AVP.
* This callback will be called when the received message contains a certain AVP.
*
* -> by command-code.
* This callback will be called when the message is a specific command (and 'R' flag).
*
* -> by application.
* This callback will be called when the message has a specific application-id.
*
* ( by vendor: would this be useful? it may be added later)
*/
enum disp_how {
DISP_HOW_ANY = 1, /* Any message. This should be only used for debug. */
DISP_HOW_APPID, /* Any message with the specified application-id */
DISP_HOW_CC, /* Messages of the specified command-code (request or answer). App id may be specified. */
DISP_HOW_AVP, /* Messages containing a specific AVP. Command-code and App id may be specified. */
DISP_HOW_AVP_ENUMVAL /* Messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */
};
/*
* Several criteria may be selected at the same time, for example command-code AND application id.
*
* If several callbacks are registered for the same object, their order is unspecified.
* The order in which the callbacks are called is:
* DISP_HOW_ANY
* DISP_HOW_AVP_ENUMVAL & DISP_HOW_AVP
* DISP_HOW_CC
* DISP_HOW_APPID
*/
/* When a callback is registered, a "when" argument is passed in addition to the disp_how value,
* to specify which values the criteria must match. */
struct disp_when {
struct dict_object * app_id;
struct dict_object * command;
struct dict_object * avp;
struct dict_object * value;
};
/* Here is the details on this "when" argument, depending on the disp_how value.
*
* DISP_HOW_ANY.
* In this case, "when" must be NULL.
*
* DISP_HOW_APPID.
* Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION.
*
* DISP_HOW_CC.
* The "command" field must be defined and point to a dictionary object of type DICT_COMMAND.
* The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id.
* The other fields are ignored.
*
* DISP_HOW_AVP.
* The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP.
* The "app_id" field may be set to restrict the messages matching to a specific app id.
* The "command" field may also be set to a valid DICT_COMMAND object.
* The content of the "value" field is ignored.
*
* DISP_HOW_AVP_ENUMVAL.
* All fields have the same constraints and meaning as in DISP_REG_AVP. In addition, the "value" field must be set
* and points to a valid DICT_ENUMVAL object.
*
* Here is a sumary of the fields: ( M : must be set; m : may be set; 0 : ignored )
* field: app_id command avp value
* APPID : M 0 0 0
* CC : m M 0 0
* AVP : m m M 0
* ENUMVA: m m M M
*/
/* The callbacks that are registered have the following prototype:
*
* int dispatch_callback( struct msg ** msg, struct avp * avp, struct session * session, int * is_answer );
*
* FUNCTION: dispatch_callback
*
* PARAMETERS:
* msg : the received message on function entry. may be updated to answer on return (see description)
* avp : for callbacks registered with DISP_HOW_AVP or DISP_HOW_AVP_ENUMVAL, direct link to the triggering AVP.
* session : if the message contains a Session-Id AVP, the corresponding session object.
*
* DESCRIPTION:
* Create a new AVP instance.
*
* RETURN VALUE:
* 0 : The AVP is created.
* EINVAL : A parameter is invalid.
* (other standard errors may be returned, too, with their standard meaning. Example:
* ENOMEM : Memory allocation for the new avp failed.)
*/
/*============================================================*/
/* MESSAGES */
/*============================================================*/
......@@ -2195,6 +2077,201 @@ int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct d
int fd_msg_update_length ( msg_or_avp * object );
/*============================================================*/
/* DISPATCH */
/*============================================================*/
/* Dispatch module (passing incoming messages to extensions registered callbacks)
* is split between the library and the daemon.
*
* The library provides the support for associating dispatch callbacks with
* dictionary objects.
*
* The daemon is responsible for calling the callbacks for a message when appropriate.
*
*
* The dispatch module has two main roles:
* - help determine if a message can be handled locally (during the routing step)
* This decision involves only the application-id of the message.
* - pass the message to the callback(s) that will handle it (during the dispatch step)
*
* The first role is handled by the daemon.
*
* About the second, these are the possibilities for registering a dispatch callback:
*
* -> For All messages.
* This callback is called for all messages that are handled locally. This should be used only
* for debug purpose.
*
* -> by AVP value (constants only).
* This callback will be called when a message is received and contains an AVP with a specified enumerated value.
*
* -> by AVP.
* This callback will be called when the received message contains a certain AVP.
*
* -> by command-code.
* This callback will be called when the message is a specific command (and 'R' flag).
*
* -> by application.
* This callback will be called when the message has a specific application-id.
*
* ( by vendor: would this be useful? it may be added later)
*/
enum disp_how {
DISP_HOW_ANY = 1, /* Any message. This should be only used for debug. */
DISP_HOW_APPID, /* Any message with the specified application-id */
DISP_HOW_CC, /* Messages of the specified command-code (request or answer). App id may be specified. */
DISP_HOW_AVP, /* Messages containing a specific AVP. Command-code and App id may be specified. */
DISP_HOW_AVP_ENUMVAL /* Messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */
};
/*
* Several criteria may be selected at the same time, for example command-code AND application id.
*
* If several callbacks are registered for the same object, they are called in the order they were registered.
* The order in which the callbacks are called is:
* DISP_HOW_ANY
* DISP_HOW_AVP_ENUMVAL & DISP_HOW_AVP
* DISP_HOW_CC
* DISP_HOW_APPID
*/
/* When a callback is registered, a "when" argument is passed in addition to the disp_how value,
* to specify which values the criteria must match. */
struct disp_when {
struct dict_object * app;
struct dict_object * command;
struct dict_object * avp;
struct dict_object * value;
};
/* Note that all the dictionary objects should really belong to the same dictionary!
*
* Here is the details on this "when" argument, depending on the disp_how value.
*
* DISP_HOW_ANY.
* In this case, "when" must be NULL.
*
* DISP_HOW_APPID.
* Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION.
*
* DISP_HOW_CC.
* The "command" field must be defined and point to a dictionary object of type DICT_COMMAND.
* The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id.
* The other fields are ignored.
*
* DISP_HOW_AVP.
* The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP.
* The "app_id" field may be set to restrict the messages matching to a specific app id.
* The "command" field may also be set to a valid DICT_COMMAND object.
* The content of the "value" field is ignored.
*
* DISP_HOW_AVP_ENUMVAL.
* All fields have the same constraints and meaning as in DISP_REG_AVP. In addition, the "value" field must be set
* and points to a valid DICT_ENUMVAL object.
*
* Here is a sumary of the fields: ( M : must be set; m : may be set; 0 : ignored )
* field: app_id command avp value
* APPID : M 0 0 0
* CC : m M 0 0
* AVP : m m M 0
* ENUMVA: m m M M
*/
enum disp_action {
DISP_ACT_CONT, /* The next handler should be called, unless *msg == NULL. */
DISP_ACT_SEND /* The updated message must be sent. No further callback is called. */
};
/* The callbacks that are registered have the following prototype:
* int dispatch_callback( struct msg ** msg, struct avp * avp, struct session * session, enum disp_action * action );
*
* CALLBACK: dispatch_callback
*
* PARAMETERS:
* msg : the received message on function entry. may be updated to answer on return (see description)
* avp : for callbacks registered with DISP_HOW_AVP or DISP_HOW_AVP_ENUMVAL, direct link to the triggering AVP.
* session : if the message contains a Session-Id AVP, the corresponding session object, NULL otherwise.
* action : upon return, this tells the daemon what to do next.
*
* DESCRIPTION:
* Called when a received message matchs the condition for which the callback was registered.
* This callback may do any kind of processing on the message, including:
* - create an answer for a request.
* - proxy a request or message, add / remove the Proxy-Info AVP, then forward the message.
* - update a routing table or start a connection with a new peer, then forward the message.
* - ...
*
* When *action == DISP_ACT_SEND on callback return, the msg pointed by *msg is passed to the routing module for sending.
* When *action == DISP_ACT_CONT, the next registered callback is called.
* When the last callback gives also DISP_ACT_CONT action value, a default handler is called. It's behavior is as follow:
* - if the message is an answer, it is discarded.
* - if the message is a request, it is passed again to the routing stack, and marked as non-local handling.
*
* RETURN VALUE:
* 0 : The callback executed successfully and updated *action appropriately.
* !0 : standard errors. In case of error, the message is discarded.
*/
/* This structure represents a handler for a registered callback, allowing its de-registration */
struct disp_hdl;
/*
* FUNCTION: fd_disp_register
*
* PARAMETERS:
* cb : The callback function to register (see dispatch_callback description above).
* how : How the callback must be registered.
* when : Values that must match, depending on the how argument.
* handle : On success, a handler to the registered callback is stored here if not NULL.
* This handler can be used to unregister the cb.
*
* DESCRIPTION:
* Register a new callback to handle messages delivered locally.
*
* RETURN VALUE:
* 0 : The callback is registered.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, enum disp_action *),
enum disp_how how, struct disp_when * when, struct disp_hdl ** handle );
/*
* FUNCTION: fd_disp_unregister
*
* PARAMETERS:
* handle : Location of the handle of the callback that must be unregistered.
*
* DESCRIPTION:
* Removes a callback previously registered by fd_disp_register.
*
* RETURN VALUE:
* 0 : The callback is unregistered.
* EINVAL : A parameter is invalid.
*/
int fd_disp_unregister ( struct disp_hdl ** handle );
/*
* FUNCTION: fd_msg_dispatch
*
* PARAMETERS:
* msg : A msg object that have already been fd_msg_parse_dict.
* session : The session corresponding to this object, if any.
* action : Upon return, the action that must be taken on the message
*
* DESCRIPTION:
* Call all handlers registered for a given message.
* The session must have already been resolved on entry.
* The msg pointed may be updated during this process.
* Upon return, the action parameter points to what must be done next.
*
* RETURN VALUE:
* 0 : Success.
* EINVAL : A parameter is invalid.
* (other errors)
*/
int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action );
/*============================================================*/
/* MESSAGE QUEUES */
......
......@@ -6,10 +6,11 @@ SET(PROJECT_COPYRIGHT "Copyright (c) 2008-2009, WIDE Project (www.wide.ad.jp) an
# List of source files for the library
SET(LFD_SRC
libfD.h
dictionary.c
dispatch.c
init.c
log.c
lists.c
dictionary.c
log.c
messages.c
mqueues.c
sessions.c
......
......@@ -409,9 +409,11 @@ static void destroy_object(struct dict_object * obj)
}
/* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */
CHECK_POSIX_DO( pthread_rwlock_wrlock(&fd_disp_lock), /* continue */ );
while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) {
fd_list_unlink( obj->disp_cbs.next );
}
CHECK_POSIX_DO( pthread_rwlock_unlock(&fd_disp_lock), /* continue */ );
/* Last, destroy the object */
free(obj);
......
/*********************************************************************************************************
* 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. *
*********************************************************************************************************/
#include "libfD.h"
/* The dispatch module in the library is quite simple: callbacks are saved in a global list
* in no particular order. In addition, they are also linked from the dictionary objects they
* refer to. */
/* Protection for the lists managed in this module. */
pthread_rwlock_t fd_disp_lock = PTHREAD_RWLOCK_INITIALIZER;
/* List of all registered handlers -- useful if we want to cleanup properly at some point... */
static struct fd_list all_handlers;
/* List of handlers registered for DISP_HOW_ANY. Other handlers are stored in the dictionary */
static struct fd_list any_handlers;
/* The structure to store a callback */
struct disp_hdl {
int eyec; /* Eye catcher, DISP_EYEC */
struct fd_list all; /* link in the all_handlers list */
struct fd_list parent;/* link in dictionary cb_list or in any_handlers */
enum disp_how how; /* Copy of registration parameter */
struct disp_when when; /* Copy of registration parameter */
int (*cb)( struct msg **, struct avp *, struct session *, enum disp_action *); /* The callback itself */
};
#define DISP_EYEC 0xD15241C1
#define VALIDATE_HDL( _hdl ) \
( ( ( _hdl ) != NULL ) && ( ((struct disp_hdl *)( _hdl ))->eyec == DISP_EYEC ) )
/**************************************************************************************/
/* Initialize the module lists */
void fd_disp_init(void)
{
TRACE_ENTRY();
fd_list_init(&all_handlers, NULL);
fd_list_init(&any_handlers, NULL);
/* if PTHREAD_RWLOCK_INITIALIZER is not supported on all platforms, we may initialize the lock here */
}
/* Call CBs from a given list (any_handlers if cb_list is NULL) -- must have locked fd_disp_lock before */
int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action,
struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu)
{
struct fd_list * senti, *li;
TRACE_ENTRY("%p %p %p %p %p %p %p %p %p", cb_list, msg, avp, sess, action, obj_app, obj_cmd, obj_avp, obj_enu);
CHECK_PARAMS(msg && action);
senti = cb_list;
if (!senti)
senti = &any_handlers;
for (li = senti->next; li != senti; li = li->next) {
struct disp_hdl * hdl = (struct disp_hdl *)(li->o);
TRACE_DEBUG(ANNOYING, "when: %p %p %p %p", hdl->when.app, hdl->when.command, hdl->when.avp, hdl->when.value);
/* Check this handler matches this message / avp */
if (hdl->when.app && (hdl->when.app != obj_app))
continue;
if (hdl->when.command && (hdl->when.command != obj_cmd))
continue;
if (hdl->when.avp && (hdl->when.avp != obj_avp))
continue;
if (hdl->when.value && (hdl->when.value != obj_enu))
continue;
/* We have a match, the cb must be called. */
CHECK_FCT( (*hdl->cb)(msg, avp, sess, action) );
if (*action != DISP_ACT_CONT)
break;
if ( *msg == NULL )
break;
}
/* We're done on this list */
return 0;
}
/**************************************************************************************/
/* Create a new handler and link it */
int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, enum disp_action *),
enum disp_how how, struct disp_when * when, struct disp_hdl ** handle )
{
struct fd_list * cb_list = NULL;
struct disp_hdl * new;
struct dict_object * type_enum, * type_avp;
struct dictionary * dict = NULL;
TRACE_ENTRY("%p %d %p %p", cb, how, when, handle);
CHECK_PARAMS( cb && ( (how == DISP_HOW_ANY) || when ));
switch (how) {
case DISP_HOW_ANY:
cb_list = &any_handlers;
break;
case DISP_HOW_APPID:
CHECK_FCT( fd_dict_disp_cb(DICT_APPLICATION, when->app, &cb_list) );
break;
case DISP_HOW_CC:
CHECK_FCT( fd_dict_disp_cb(DICT_COMMAND, when->command, &cb_list) );
break;
case DISP_HOW_AVP_ENUMVAL:
CHECK_FCT( fd_dict_disp_cb(DICT_ENUMVAL, when->value, &cb_list) ); /* cb_list is then overwriten */
CHECK_FCT( fd_dict_getdict(when->value, &dict) );
CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_ENUMVAL, when->value, &type_enum, EINVAL) );
case DISP_HOW_AVP:
CHECK_FCT( fd_dict_disp_cb(DICT_AVP, when->avp, &cb_list) );
if (dict) {
CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, when->avp, &type_avp, EINVAL) );
CHECK_PARAMS( type_enum == type_avp );
}
break;
default:
CHECK_PARAMS(how = 0);
}
/* We might further check optional fields, but we trust the caller ^^ */
/* Create the new handler */
CHECK_MALLOC( new = malloc( sizeof(struct disp_hdl) ) );
memset(new, 0, sizeof(struct disp_hdl));
new->eyec = DISP_EYEC;
fd_list_init(&new->all, new);
fd_list_init(&new->parent, new);
new->how = how;
switch (how) {
case DISP_HOW_AVP_ENUMVAL:
new->when.value = when->value;
case DISP_HOW_AVP:
new->when.avp = when->avp;
case DISP_HOW_CC:
new->when.command = when->command;
case DISP_HOW_APPID:
new->when.app = when->app;
}
new->cb = cb;
/* Now, link this new element in the appropriate lists */
CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) );
fd_list_insert_before(&all_handlers, &new->all);
fd_list_insert_before(cb_list, &new->parent);
CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) );
/* We're done */
if (handle)
*handle = new;
return 0;
}
/* Delete a handler */
int fd_disp_unregister ( struct disp_hdl ** handle )
{
struct disp_hdl * del;
TRACE_ENTRY("%p", handle);
CHECK_PARAMS( handle && VALIDATE_HDL(*handle) );
del = *handle;
*handle = NULL;
CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) );
fd_list_unlink(&del->all);
fd_list_unlink(&del->parent);
CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) );
free(del);
return 0;
}
......@@ -48,6 +48,7 @@ int fd_lib_init(void)
/* Initialize the modules that need it */