Navigation


Changeset 1098:f38d77f9cfd3 in freeDiameter


Ignore:
Timestamp:
May 9, 2013, 12:59:34 PM (11 years ago)
Author:
Sebastien Decugis <sdecugis@freediameter.net>
Branch:
default
Phase:
public
Message:

Initial implementation of the hook mechanism

Files:
8 edited

Legend:

Unmodified
Added
Removed
  • include/freeDiameter/freeDiameter-host.h.in

    r935 r1098  
    9595#endif /* FD_DEFAULT_CONF_FILENAME */
    9696
     97/* Maximum number of hooks handlers that can be registered. Make this compilation option if needed */
     98#define FD_HOOK_HANDLE_LIMIT    5
     99
    97100#ifdef __cplusplus
    98101}
  • include/freeDiameter/libfdcore.h

    r1088 r1098  
    879879/* These functions allow an extension to collect state information about the
    880880 * framework, as well as being hooked at some key checkpoints in the processing
    881  * for logging / statistics purpose.
     881 * for logging or statistics purpose.
    882882 */
    883883 
     
    887887 * PARAMETERS:
    888888 *  type        : The type of hook that triggered this call, in case same cb is registered for several hooks.
    889  *  msg         : If relevant, the pointer to the message trigging the call. NULL otherwise.
     889 *  msg         : If relevant, the pointer to the message triggering the call. NULL otherwise.
    890890 *  peer        : If relevant, the pointer to the peer associated with the call. NULL otherwise.
    891891 *  other       : For some callbacks, the remaining information is passed in this parameter. See each hook detail.
    892892 *  permsgdata  : Structure associated with a given message, across several hooks.
    893  *                 Same structure is associated with requests and corresponding answers.
     893 *                 A different structure is associated with requests and corresponding answers.
    894894 *                 See fd_hook_data_hdl below for details.
    895895 *                 If no fd_hook_data_hdl is registered with this callback, this parameter is always NULL
     
    916916                 - {msg} is NULL.
    917917                 - {peer} is NULL.
    918                  - {*other} is NULL, {other} points to a valid location where you can store a pointer.
    919                     This same pointer will then passed to the next hook, once message is processed.
    920                     IMPORTANT: free() will be called on this pointer if any problem, so this pointer must be malloc'd.
    921                  - {permsgdata} is NULL.
     918                 - {other} is a pointer to a structure { size_t len; uint8_t * buf; } containing the received buffer.
     919                 - {permsgdata} points to either a new empty structure allocated for this message (cf. fd_hook_data_hdl), or NULL if no hdl is registered.
    922920                 */
    923921                 
     
    928926                 - {peer} is set if the message is received from a peer's connection, and NULL if the message is from a new client
    929927                   connected and not yet identified
    930                  - {*other} contains the pointer stored with the HOOK_DATA_RECEIVED hook if any, NULL otherwise. After this hook returns, free(*other) is called if not NULL.
    931                  - {permsgdata} points to either a new empty structure allocated for this request (cf. fd_hook_data_hdl), or the request's existing structure if the message is an answer.
     928                 - {other} is NULL.
     929                 - {permsgdata} points to either a new empty structure allocated for this message or the one passed to HOOK_DATA_RECEIVED if used.
    932930                 */
    933931       
     
    946944                   try to call fd_msg_parse_dict, it will slow down the operation of a relay agent.
    947945                 - {peer} is set if the message is sent to a peer's connection, and NULL if the message is sent to a new client
    948                    connected and not yet identified / being rejected
     946                   connected and not yet identified, or being rejected
    949947                 - {other} is NULL.
    950948                 - {permsgdata} points to existing structure if any, or a new structure otherwise.
    951                     If the message is an answer, the structure is shared with the corresponding request.
    952949                 */
    953950       
     
    959956                 - {peer} is the peer this message was previously sent to.
    960957                 - {other} is NULL.
    961                  - {permsgdata} points to existing structure associated with this request.
     958                 - {permsgdata} points to existing structure if any, or a new structure otherwise.
    962959                 */
    963960       
     
    994991       
    995992        HOOK_MESSAGE_DROPPED,
    996                 /* Hook called when a message is being discarded by the framework because of some error.
    997                    It is probably a good idea to log this for analysis.
     993                /* Hook called when a message is being discarded by the framework because of some error condition (normal or abnormal).
     994                   It is probably a good idea to log this for analysis / backup.
    998995                 - {msg} points to the message, which will be freed as soon as the hook returns.
    999996                 - {peer} is NULL.
     
    10221019
    10231020
    1024 /* Type if the {permsgdata} ointer. It is up to each extension to define its own structure. This is opaque for the framework. */
     1021/* Type if the {permsgdata}. It is up to each extension to define its own structure. This is opaque for the framework. */
    10251022struct fd_hook_permsgdata;
    10261023
     
    10351032 *
    10361033 * PARAMETERS:
    1037  *  permsgdata_new_cb     : function called to initialize a new empty fd_hook_permsgdata structure, when a hook will be called for a message with not structure yet. If the function returns NULL, it will be called again for the next hook.
    1038  *  permsgdata_destroy_cb : function called when a message is being disposed. It should free the resources associated with the fd_hook_permsgdata.
    1039  *  new_handle            : On success, a handler to the registered callback is stored here.
     1034 *  permsgdata_size     : the size of the fd_hook_permsgdata structure.
     1035 *  permsgdata_init_cb  : function called to initialize a new fd_hook_permsgdata structure, when a hook will be called for a message that does not have such structure yet.
     1036 *                           The memory is already allocated and blanked, so you can pass NULL if no further handling is required.
     1037 *  permsgdata_fini_cb  : function called when a message is being disposed. It should free the resources associated with the fd_hook_permsgdata.
     1038 *                           You can pass NULL if no special handling is required. The memory of the permsgdata structure itself will be freed by the framework.
     1039 *  new_handle          : On success, a handler to the registered callback is stored here.
    10401040 *                           This handler will be used to unregister the cb.
    10411041 *
    10421042 * DESCRIPTION:
    10431043 *   Register a new fd_hook_data_hdl. This handle is used during hooks registration (see below) in order to associate data with the messages, to allow keeping tracking of the message easily.
    1044  *  Note that these handlers are statically allocated and cannot be unregistered.
     1044 *  Note that these handlers are statically allocated and cannot be unregistered. FD_HOOK_HANDLE_LIMIT handlers can be registered at maximum (recompile libfdproto if you change this value)
    10451045 *
    10461046 * RETURN VALUE:
     
    10501050 */
    10511051int fd_hook_data_register(
    1052         struct fd_hook_permsgdata * (*permsgdata_new_cb)     (void),
    1053         void (*permsgdata_destroy_cb) (struct fd_hook_permsgdata *),
    1054         struct fd_hook_data_hdl **    new_handle
     1052        size_t permsgdata_size,
     1053        void (*permsgdata_init_cb) (struct fd_hook_permsgdata *),
     1054        void (*permsgdata_fini_cb) (struct fd_hook_permsgdata *),
     1055        struct fd_hook_data_hdl **new_handle
    10551056);
    1056 
    10571057
    10581058/* A handler associated with a registered hook callback (for cleanup) */
     
    10631063 *
    10641064 * PARAMETERS:
    1065  *  type          : The fd_hook_type for which this cb is registered. Call several times if you want to register for several hooks.
     1065 *  type_mask     : A bitmask of fd_hook_type bits for which this cb is registered, e.g. ((1 << HOOK_MESSAGE_RECEIVED) || (1 << HOOK_MESSAGE_SENT))
    10661066 *  fd_hook_cb    : The callback function to register (see prototype above).
    10671067 *  regdata       : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
     
    10791079 *  ENOMEM      : Not enough memory to complete the operation
    10801080 */
    1081 int fd_hook_register (  enum fd_hook_type type,
    1082                         void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, void * regdata),
    1083                         void * regdata,
     1081int fd_hook_register (  uint32_t type_mask,
     1082                        void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata),
     1083                        void  *regdata,
    10841084                        struct fd_hook_data_hdl *data_hdl,
    10851085                        struct fd_hook_hdl ** handler );
  • include/freeDiameter/libfdproto.h

    r1097 r1098  
    25502550
    25512551
     2552/* Helper for the hooks mechanism, for use from libfdcore */
     2553struct fd_msg_pmdl {
     2554        struct fd_list sentinel; /* if the sentinel.o field is NULL, the structure is not initialized. Otherwise it points to the cleanup function in libfdcore. */
     2555        pthread_mutex_t lock;
     2556};
     2557#define FD_MSG_PMDL_INITIALIZER(pmdl_ptr)        { FD_LIST_INITIALIZER(  (pmdl_ptr)->sentinel       ), PTHREAD_MUTEX_INITIALIZER }
     2558struct fd_msg_pmdl * fd_msg_pmdl_get(struct msg * msg);
     2559
    25522560/***************************************/
    25532561/*   Manage AVP values                 */
  • libfdcore/cnxctx.c

    r1084 r1098  
    736736                }
    737737               
     738                // fd_msg_log(....)
     739               
    738740                /* We have received a complete message, pass it to the daemon */
    739741                CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */);
  • libfdcore/core.c

    r1085 r1098  
    187187       
    188188        /* Initialize some modules */
     189        CHECK_FCT( fd_hooks_init()  );
    189190        CHECK_FCT( fd_queues_init() );
    190191        CHECK_FCT( fd_msg_init()    );
  • libfdcore/fdcore-internal.h

    r1085 r1098  
    363363#define FD_CNX_ORDERED          (1 << 0)        /* All messages sent with this flag set will be delivered in the same order. No guarantee on other messages */
    364364
     365/* Internal calls of the hook mechanism */
     366void   fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl);
     367void   fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl);
     368int    fd_hooks_init(void);
    365369#endif /* _FDCORE_INTERNAL_H */
  • libfdcore/hooks.c

    r1077 r1098  
    3636#include "fdcore-internal.h"
    3737
    38 struct fd_hook_hdl;
    39 struct fd_hook_data_hdl;
    40 
     38/* Structures for the fd_hook_data_hdl management */
     39static struct fd_hook_data_hdl {
     40        size_t  pmd_size;
     41        void  (*pmd_init_cb)(struct fd_hook_permsgdata *);
     42        void  (*pmd_fini_cb)(struct fd_hook_permsgdata *);
     43} HDH_array[FD_HOOK_HANDLE_LIMIT];
     44static int max_index = 0;
     45static pthread_mutex_t HDH_lock = PTHREAD_MUTEX_INITIALIZER;
     46
     47/* The structure linked from the msg structure list */
     48struct pmd_list_item {
     49        struct fd_list  chain;          /* this list is ordered by hdl */
     50        struct fd_hook_data_hdl * hdl;
     51        struct fd_hook_permsgdata { } pmd; /* this data belongs to the extension; we only know the size of it */
     52};
     53
     54#define sizeof_pmd(hdl) (((size_t)&((struct pmd_list_item *)0)->pmd) + hdl->pmd_size)
     55
     56/* Now a hook registered by an extension */
     57struct fd_hook_hdl {
     58        struct fd_list chain[HOOK_PEER_LAST+1];
     59        void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata);
     60        void  *regdata;
     61        struct fd_hook_data_hdl *data_hdl;
     62};
     63
     64/* Array of those hooks */
     65struct {
     66        struct fd_list sentinel;
     67        pthread_rwlock_t rwlock;
     68} HS_array[HOOK_PEER_LAST+1];
     69
     70/* Initialize the array of sentinels for the hooks */
     71int fd_hooks_init(void)
     72{
     73        int i;
     74        for (i=0; i <= HOOK_PEER_LAST; i++) {
     75                fd_list_init(&HS_array[i].sentinel, NULL);
     76                CHECK_POSIX( pthread_rwlock_init(&HS_array[i].rwlock, NULL) );
     77        }
     78        return 0;
     79}
     80
     81/* Get a slot in the array */
    4182int fd_hook_data_register(
    42         struct fd_hook_permsgdata * (*permsgdata_new_cb)     (void),
    43         void (*permsgdata_destroy_cb) (struct fd_hook_permsgdata *),
    44         struct fd_hook_data_hdl **    new_handle
    45 )
    46 {
    47         return ENOTSUP;
    48 }
    49 
    50 int fd_hook_register (  enum fd_hook_type type,
    51                         void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, void * regdata),
    52                         void * regdata,
     83        size_t permsgdata_size,
     84        void (*permsgdata_init_cb) (struct fd_hook_permsgdata *),
     85        void (*permsgdata_fini_cb) (struct fd_hook_permsgdata *),
     86        struct fd_hook_data_hdl **new_handle)
     87{
     88        int ret = ENOSPC, idx;
     89        TRACE_ENTRY("%zd %p %p %p", permsgdata_size, permsgdata_init_cb, permsgdata_fini_cb, new_handle);
     90       
     91        CHECK_PARAMS( permsgdata_size && new_handle );
     92       
     93        CHECK_POSIX( pthread_mutex_lock(&HDH_lock) );
     94        if (max_index < FD_HOOK_HANDLE_LIMIT) {
     95                idx = max_index++;
     96                ret = 0;
     97        }
     98        CHECK_POSIX( pthread_mutex_unlock(&HDH_lock) );
     99       
     100        if (ret == 0) {
     101                HDH_array[idx].pmd_size = permsgdata_size;
     102                HDH_array[idx].pmd_init_cb = permsgdata_init_cb;
     103                HDH_array[idx].pmd_fini_cb = permsgdata_fini_cb;
     104                *new_handle = &HDH_array[idx];
     105        }
     106       
     107        return ret;
     108}
     109
     110/* Register a new hook callback */
     111int fd_hook_register (  uint32_t type_mask,
     112                        void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata),
     113                        void  *regdata,
    53114                        struct fd_hook_data_hdl *data_hdl,
    54115                        struct fd_hook_hdl ** handler )
    55116{
    56         return ENOTSUP;
    57 }
    58 
    59 
     117        struct fd_hook_hdl * newhdl = NULL;
     118        int i;
     119       
     120        TRACE_ENTRY("%x %p %p %p %p", type_mask, fd_hook_cb, regdata, data_hdl, handler);
     121       
     122        CHECK_PARAMS( fd_hook_cb && handler );
     123       
     124        CHECK_MALLOC( newhdl = malloc(sizeof(struct fd_hook_hdl)) );
     125        memset(newhdl, 0, sizeof(struct fd_hook_hdl));
     126       
     127        newhdl->fd_hook_cb = fd_hook_cb;
     128        newhdl->regdata = regdata;
     129        newhdl->data_hdl = data_hdl;
     130       
     131        for (i=0; i <= HOOK_PEER_LAST; i++) {
     132                fd_list_init(&newhdl->chain[i], newhdl);
     133                if (type_mask & (1<<i)) {
     134                        CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
     135                        fd_list_insert_before( &HS_array[i].sentinel, &newhdl->chain[i]);
     136                        CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
     137                }
     138        }
     139       
     140        *handler = newhdl;
     141        return 0;
     142}
     143
     144/* free this hook callback */
    60145int fd_hook_unregister( struct fd_hook_hdl * handler )
    61146{
    62         return ENOTSUP;
    63 }
    64 
     147        int i;
     148        TRACE_ENTRY("%p", handler);
     149        CHECK_PARAMS( handler );
     150       
     151        for (i=0; i <= HOOK_PEER_LAST; i++) {
     152                if ( ! FD_IS_LIST_EMPTY(&handler->chain[i])) {
     153                        CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
     154                        fd_list_unlink(&handler->chain[i]);
     155                        CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
     156                }
     157        }
     158       
     159        free(handler);
     160       
     161        return 0;
     162}
     163
     164/* callback for the libfdproto to free the data associated with a message */
     165static void pmdl_free(struct fd_msg_pmdl *pmdl)
     166{
     167        /* destroy all the items in the list */
     168        while (!FD_IS_LIST_EMPTY(&pmdl->sentinel)) {
     169                struct pmd_list_item * li = (struct pmd_list_item *)(pmdl->sentinel.next);
     170                if (li->hdl->pmd_fini_cb) {
     171                        (*li->hdl->pmd_fini_cb)(&li->pmd);
     172                }
     173                fd_list_unlink(&li->chain);
     174                free(li);
     175        }
     176        CHECK_POSIX_DO( pthread_mutex_destroy(&pmdl->lock), );
     177        pmdl->sentinel.o = NULL;
     178}
     179
     180/* Save the list of pmd into the message structure, as well as the callback to free this list */
     181void   fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl)
     182{
     183        struct fd_msg_pmdl * in_msg;
     184       
     185        CHECK_PARAMS_DO( msg && pmdl, return );
     186        in_msg = fd_msg_pmdl_get(msg);
     187        ASSERT(in_msg && (in_msg->sentinel.o == NULL)); /* error / already initialized ??? */
     188        fd_list_init(&in_msg->sentinel, pmdl_free);
     189        CHECK_POSIX_DO( pthread_mutex_init(&in_msg->lock, NULL), );
     190        /* Now move all items from the pmdl pointer into the initialized list */
     191        CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
     192        fd_list_move_end(&in_msg->sentinel, &pmdl->sentinel);
     193        CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
     194        pmdl_free(pmdl);
     195        /* We're done */
     196}
     197
     198/* Return the location of the permsgdata area corresponding to this handle, after eventually having created it. Return NULL in case of failure */
     199static struct fd_hook_permsgdata * get_or_create_pmd(struct fd_msg_pmdl *pmdl, struct fd_hook_hdl * h)
     200{
     201        struct fd_hook_permsgdata * ret = NULL;
     202        struct fd_list * li;
     203        CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
     204       
     205        /* Search in the list for an item with the same handle. The list is ordered by this handle */
     206        for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
     207                struct pmd_list_item * pli = (struct pmd_list_item *) li;
     208                if (pli->hdl == h->data_hdl)
     209                        ret = &pli->pmd;
     210                if (pli->hdl >= h->data_hdl)
     211                        break;
     212        }
     213        if (!ret) {
     214                /* we need to create a new one and insert before li */
     215                struct pmd_list_item * pli;
     216                CHECK_MALLOC_DO( pli = malloc(sizeof_pmd(h->data_hdl)), );
     217                if (pli) {
     218                        memset(pli, 0, sizeof_pmd(h->data_hdl));
     219                        fd_list_init(&pli->chain, pli);
     220                        pli->hdl = h->data_hdl;
     221                        ret = &pli->pmd;
     222                        if (h->data_hdl->pmd_init_cb) {
     223                                (*h->data_hdl->pmd_init_cb)(ret);
     224                        }
     225                        fd_list_insert_before(li, &pli->chain);
     226                }
     227        }
     228       
     229        CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
     230        return ret;
     231}
     232
     233
     234/* The function that does the work of calling the extension's callbacks and also managing the permessagedata structures */
     235void   fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl)
     236{
     237        struct fd_list * li;
     238        ASSERT(type <= HOOK_PEER_LAST);
     239       
     240        /* lock the list of hooks for this type */
     241        CHECK_POSIX_DO( pthread_rwlock_rdlock(&HS_array[type].rwlock), );
     242       
     243        /* for each registered hook */
     244        for (li = HS_array[type].sentinel.next; li != &HS_array[type].sentinel; li = li->next) {
     245                struct fd_hook_hdl * h = (struct fd_hook_hdl *)li->o;
     246                struct fd_hook_permsgdata * pmd = NULL;
     247               
     248                /* do we need to handle pmd ? */
     249                if (h->data_hdl && pmdl) {
     250                        pmd = get_or_create_pmd(pmdl, h);
     251                }
     252               
     253                /* Now, call this callback */
     254                (*h->fd_hook_cb)(type, msg, &peer->p_hdr, other, pmd, h->regdata);
     255        }
     256       
     257        /* done */
     258        CHECK_POSIX_DO( pthread_rwlock_unlock(&HS_array[type].rwlock), );
     259}
  • libfdproto/messages.c

    r1096 r1098  
    137137        DiamId_t                 msg_src_id;            /* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */
    138138        size_t                   msg_src_id_len;        /* cached length of this string */
    139        
     139        struct fd_msg_pmdl       msg_pmdl;              /* list of permessagedata structures. */
    140140};
    141141
     
    674674        }
    675675       
     676        if ((obj->type == MSG_MSG) && (_M(obj)->msg_pmdl.sentinel.o != NULL)) {
     677                ((void (*)(struct fd_msg_pmdl *))_M(obj)->msg_pmdl.sentinel.o)(&_M(obj)->msg_pmdl);
     678        }
     679       
    676680        /* free the object */
    677681        free(obj);
     
    14961500       
    14971501        return 0;
     1502}
     1503
     1504/* Retrieve the location of the pmd list for the message; return NULL if failed */
     1505struct fd_msg_pmdl * fd_msg_pmdl_get(struct msg * msg)
     1506{
     1507        CHECK_PARAMS_DO( CHECK_MSG(msg), return NULL );
     1508        return &msg->msg_pmdl;
    14981509}
    14991510
Note: See TracChangeset for help on using the changeset viewer.