# HG changeset patch # User Sebastien Decugis # Date 1259819963 -32400 # Node ID e5fcd672caff09ea5f47091cc3c056773d07081c # Parent 03740d31e56eec98f3a3a1d4960b6bb29a1b56b2 Added new function to retrieve messages sessions easily diff -r 03740d31e56e -r e5fcd672caff freeDiameter/routing.c --- a/freeDiameter/routing.c Wed Dec 02 18:28:49 2009 +0900 +++ b/freeDiameter/routing.c Thu Dec 03 14:59:23 2009 +0900 @@ -51,3 +51,9 @@ { return ENOTSUP; } + +int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler ); +int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata ); + +int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler ); +int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata ); diff -r 03740d31e56e -r e5fcd672caff include/freeDiameter/freeDiameter.h --- a/include/freeDiameter/freeDiameter.h Wed Dec 02 18:28:49 2009 +0900 +++ b/include/freeDiameter/freeDiameter.h Thu Dec 03 14:59:23 2009 +0900 @@ -444,35 +444,172 @@ * - the PSM thread parses the buffer, does some verifications, handles non routable messages (fd_msg_is_routable) * - routable messages are queued in the fd_g_incoming global queue. * - a thread (routing-in) picks the message and takes the decision if it is handled locally or forwarded, - * based on local capabilities (registered by extensions). + * based on local capabilities (registered by extensions with fd_disp_app_support). * - If the message is handled locally, it is queued in fd_g_local. * - Another thread (dispatch.c) will handle this message and pass it to registered callbacks (see fd_disp_register in libfreeDiameter.h). * * (*) FWD messages details: * - The process is the same as for IN messages, until the routing-in threads makes its decision that the message is not handled locally. + * - If the local peer does not relay message, an error DIAMETER_APPLICATION_UNSUPPORTED is returned. * - All callbacks registered with fd_rt_fwd_register are called for the message (see bellow). * - these callbacks will typically do proxying work. Note that adding the route-record is handled by the daemon. * - Once all callbacks have been called, the message is queued in the global fd_g_outgoing queue. * - The remaining processing is the same as for OUT messages, as described bellow. * * (*) OUT messages details: - * - The message are picked from fd_g_outgoing, as result of forwarding process or call to fd_msg_send. - * - The (routing-out) thread builds a list of possible destinations for the message. - * The logic to build this list is as follow: + * - The message are picked from fd_g_outgoing (they are queued there as result of forwarding process or call to fd_msg_send.) + * - The (routing-out) thread builds a list of possible destinations for the message, as follow: * - create a list of all known peers in the "OPEN" state. * - remove from that list all peers that are in a Route-Record AVP of the message, to avoid routing loops. * - remove also all peers that have previously replied an error message for this message. - * - If the list is empty, create an error UNABLE_TO_DELIVER (note: should we trig dynamic discovery here???) and reply this. + * - If the list is empty, create an error UNABLE_TO_DELIVER (note: should we trig dynamic discovery here???) and reply. * - Otherwise, call all callbacks registered by function fd_rt_out_register, with the list of peers and the message. * - Order the resulting list of peers by score (see bellow), and sent the message to the peer with highest (positive) score. * - in case the peer is no longer in the "OPEN" state, send the message to the second peer in the list. * - if no peer is in OPEN state anymore, restart the process of creating the list. - * - The peer thread will handle the creation of the Hop-by-hop ID and sending the message. + * - Once a peer has been selected, the message is queued into that peer's outgoing queue. + * + * The following functions allow an extension to register or remove a callback as described above. + */ + +/********** Forwarding callbacks: for Proxy operations ***********/ + +/* Handle to registered callback */ +struct fd_rt_fwd_hdl; + +/* Message direction for the callback */ +enum fd_rt_fwd_dir { + RT_FWD_REQ = 1, /* The callback will be called on forwarded requests only */ + RT_FWD_ANS, /* The callback will be called on answers and errors only */ + RT_FWD_ALL, /* The callback will be called on all forwarded messages */ +}; + +/* + * FUNCTION: fd_rt_fwd_register * - * This part of the API (routing-api.h) provides the definitions of the rt_out_cb_t and rt_fwd_cb_t callbacks, and the - * functions to register and deregister these callbacks. + * PARAMETERS: + * rt_fwd_cb : The callback function to register (see prototype bellow). + * cbdata : Pointer to pass to the callback when it is called. The data is opaque to the daemon. + * dir : One of the RT_FWD_* directions defined above. + * handler : On success, a handler to the registered callback is stored here. + * This handler will be used to unregister the cb. + * + * DESCRIPTION: + * Register a new callback for forwarded messages. See explanations above. + * + * RETURN VALUE: + * 0 : The callback is registered. + * EINVAL : A parameter is invalid. + * ENOMEM : Not enough memory to complete the operation + */ +int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler ); +/* + * CALLBACK: rt_fwd_cb + * + * PARAMETERS: + * data : pointer to some data that was passed when the callback was registered (optional). + * msg : The message that is being forwarded. + * + * DESCRIPTION: + * This callback is called when a message is forwarded to another peer. It may for example add a Proxy-Info AVP. + * The callback may also choose to handle the message in a more complex form. In that case, it must set *msg = NULL + * and handle it differently. In such case, the forwarding thread will stop processing this message. + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : An error occurred -- will result in daemon's termination. */ +/* + * FUNCTION: fd_rt_fwd_unregister + * + * PARAMETERS: + * handler : The handler of the callback that must be unregistered. + * cbdata : Will receive the data registered with the callback, that can be freed if needed. + * + * DESCRIPTION: + * Removes a callback from the list of registered callbacks. + * + * RETURN VALUE: + * 0 : The callback is unregistered. + * EINVAL : A parameter is invalid. + */ +int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata ); + + +/********** Out callbacks: for next hop routing decision operations ***********/ + +/* Handle to registered callback */ +struct fd_rt_out_hdl; + +enum fd_rt_out_score { + FD_SCORE_NO_DELIVERY = -70, /* We should not send this message to this candidate */ + FD_SCORE_LOAD_BALANCE = 1, /* Use this to differentiate between several peers with the same score */ + FD_SCORE_DEFAULT = 5, /* The peer is a default route for all messages */ + FD_SCORE_DEFAULT_REALM = 10, /* The peer is a default route for this realm */ + FD_SCORE_REDIR_HOST = 25, /* If there is a redirect rule with ALL_HOST for these message and peer */ + FD_SCORE_REDIR_APP = 30, /* If there is a redirect rule with ALL_APPLICATION for these message and peer */ + FD_SCORE_REDIR_REALM = 35, /* If there is a redirect rule with ALL_REALM for these message and peer */ + FD_SCORE_REDIR_REALM_APP = 40, /* If there is a redirect rule with REALM_AND_APPLICATION for these message and peer */ + FD_SCORE_REDIR_USER = 45, /* If there is a redirect rule with ALL_USER for these message and peer */ + FD_SCORE_REDIR_SESSION = 50, /* If there is a redirect rule with ALL_SESSION for these message and peer */ + FD_SCORE_FINALDEST = 100 /* If the peer is the final recipient of the message, it receives a big score. */ +}; + +/* + * FUNCTION: fd_rt_out_register + * + * PARAMETERS: + * rt_out_cb : The callback function to register (see prototype bellow). + * cbdata : Pointer to pass to the callback when it is called. The data is opaque to the daemon. + * priority : Order for calling this callback. The callbacks are called in reverse priority order (higher priority = called sooner). + * handler : On success, a handler to the registered callback is stored here. + * This handler will be used to unregister the cb. + * + * DESCRIPTION: + * Register a new callback to handle OUT routing decisions. See explanations above. + * + * RETURN VALUE: + * 0 : The callback is registered. + * EINVAL : A parameter is invalid. + * ENOMEM : Not enough memory to complete the operation + */ +int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler ); +/* + * CALLBACK: rt_out_cb + * + * PARAMETERS: + * cbdata : pointer to some data that was registered with the callback. + * msg : The message that must be sent. + * list : The list of peers to which the message may be sent to, as returned by fd_rtd_candidate_extract + * + * DESCRIPTION: + * This callback must attribute a score (preferably from FD_SCORE_*) to each candidate peer in the list. + * Once all registered callbacks have been called, the message is sent to the candidate with the highest score. + * Note that each callback must *add* its locally-attributed score to the candidate current "score" parameter, not replace it! + * Note also that this callback must be re-entrant since it may be called by several threads at the same time + * (for different messages) + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : An error occurred. + */ + +/* + * FUNCTION: fd_rt_out_unregister + * + * PARAMETERS: + * handler : The handler of the callback that must be unregistered. + * cbdata : Will receive the data registered with the callback, that can be freed if needed. + * + * DESCRIPTION: + * Removes a callback from the list of registered callbacks. + * + * RETURN VALUE: + * 0 : The callback is unregistered. + * EINVAL : A parameter is invalid. + */ +int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata ); /***************************************/ diff -r 03740d31e56e -r e5fcd672caff include/freeDiameter/libfreeDiameter.h --- a/include/freeDiameter/libfreeDiameter.h Wed Dec 02 18:28:49 2009 +0900 +++ b/include/freeDiameter/libfreeDiameter.h Thu Dec 03 14:59:23 2009 +0900 @@ -1472,8 +1472,7 @@ * new : if not NULL, set to 1 on return if the session object has been created, 0 if it was simply retrieved. * * DESCRIPTION: - * Retrieve a session object from a Session-Id string. Calling this function makes an implicit call to the - * fd_sess_link function on the returned session. In case no session object was previously existing with this + * Retrieve a session object from a Session-Id string. In case no session object was previously existing with this * id, a new object is silently created (equivalent to fd_sess_new with flag SESSION_NEW_FULL). * * RETURN VALUE: @@ -1647,6 +1646,8 @@ /* Reorder the list of peers */ int fd_rtd_candidate_reorder(struct fd_list * candidates); +/* Note : it is fine for a callback to add a new entry in the candidates list after the list has been extracted. The diamid must then be malloc'd. */ +/* Beware that this could lead to routing loops */ /*============================================================*/ /* MESSAGES */ @@ -2051,6 +2052,26 @@ uint32_t fd_msg_eteid_get ( void ); +/* + * FUNCTION: fd_msg_sess_get + * + * PARAMETERS: + * dict : the dictionary that contains the Session-Id AVP definition + * msg : A valid message. + * session : Location to store the session pointer when retrieved. + * new : Indicates if the session has been created. + * + * DESCRIPTION: + * This function retrieves or creates the session object corresponding to a message. + * If the message does not contain a Session-Id AVP, *session == NULL on return. + * Note that the Session-Id AVP must never be modified after created in a message. + * + * RETURN VALUE: + * 0 : success + * !0 : standard error code. + */ +int fd_msg_sess_get(struct dictionary * dict, struct msg * msg, struct session ** session, int * new); + /***************************************/ /* Manage AVP values */ /***************************************/ diff -r 03740d31e56e -r e5fcd672caff libfreeDiameter/libfD.h --- a/libfreeDiameter/libfD.h Wed Dec 02 18:28:49 2009 +0900 +++ b/libfreeDiameter/libfD.h Thu Dec 03 14:59:23 2009 +0900 @@ -55,4 +55,8 @@ struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu); extern pthread_rwlock_t fd_disp_lock; +/* Messages / sessions API */ +int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new); +int fd_sess_reclaim_msg ( struct session ** session ); + #endif /* _LIBFD_H */ diff -r 03740d31e56e -r e5fcd672caff libfreeDiameter/messages.c --- a/libfreeDiameter/messages.c Wed Dec 02 18:28:49 2009 +0900 +++ b/libfreeDiameter/messages.c Thu Dec 03 14:59:23 2009 +0900 @@ -118,6 +118,7 @@ int msg_routable; /* Is this a routable message? (0: undef, 1: routable, 2: non routable) */ struct msg *msg_query; /* the associated query if the message is a received answer */ struct rt_data *msg_rtdata; /* Routing list for the query */ + struct session *msg_sess; /* Cached message session if any */ struct { void (*fct)(void *, struct msg **); void * data; @@ -572,6 +573,10 @@ fd_rtd_free(&_M(obj)->msg_rtdata); } + if ((obj->type == MSG_MSG) && (_M(obj)->msg_sess != NULL)) { + CHECK_FCT_DO( fd_sess_reclaim_msg ( &_M(obj)->msg_sess ), /* continue */); + } + /* free the object */ free(obj); @@ -660,8 +665,8 @@ msg->msg_public.msg_hbhid, msg->msg_public.msg_eteid ); - fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p src:%s\n", - INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_src_id?:"(nil)"); + fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p sess:%p src:%s\n", + INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_sess, msg->msg_src_id?:"(nil)"); } #define DUMP_VALUE(_format, _parms...) fd_log_debug(INOBJHDR "value : t:'%s' v:'" _format "'\n", INOBJHDRVAL, typename, ## _parms); @@ -1128,6 +1133,57 @@ return 0; } +/* Retrieve the session of the message */ +int fd_msg_sess_get(struct dictionary * dict, struct msg * msg, struct session ** session, int * new) +{ + struct avp * avp; + + TRACE_ENTRY("%p %p %p", msg, session, new); + + /* Check we received valid parameters */ + CHECK_PARAMS( CHECK_MSG(msg) ); + CHECK_PARAMS( session ); + + /* If we already resolved the session, just send it back */ + if (msg->msg_sess) { + *session = msg->msg_sess; + if (new) + *new = 0; + return 0; + } + + /* OK, we have to search for Session-Id AVP -- it is usually the first AVP, but let's be permissive here */ + /* -- note: we accept messages that have not yet been dictionary parsed... */ + CHECK_FCT( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) ); + while (avp) { + if ( (avp->avp_public.avp_code == AC_SESSION_ID) + && (avp->avp_public.avp_vendor == 0) ) + break; + + /* Otherwise move to next AVP in the message */ + CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); + } + + if (!avp) { + TRACE_DEBUG(FULL, "No Session-Id AVP found in message %p", msg); + *session = NULL; + return 0; + } + + if (!avp->avp_model) { + CHECK_FCT( fd_msg_parse_dict ( avp, dict ) ); + } + + ASSERT( avp->avp_public.avp_value ); + + /* Resolve the session and we are done */ + CHECK_FCT( fd_sess_fromsid_msg ( avp->avp_public.avp_value->os.data, avp->avp_public.avp_value->os.len, &msg->msg_sess, new) ); + *session = msg->msg_sess; + + return 0; +} + + /******************* End-to-end counter *********************/ uint32_t fd_eteid; pthread_mutex_t fd_eteid_lck = PTHREAD_MUTEX_INITIALIZER; diff -r 03740d31e56e -r e5fcd672caff libfreeDiameter/sessions.c --- a/libfreeDiameter/sessions.c Wed Dec 02 18:28:49 2009 +0900 +++ b/libfreeDiameter/sessions.c Thu Dec 03 14:59:23 2009 +0900 @@ -99,6 +99,7 @@ pthread_mutex_t stlock; /* A lock to protect the list of states associated with this session */ struct fd_list states; /* Sentinel for the list of states of this session. */ + int msg_cnt;/* Reference counter for the messages pointing to this session */ }; /* Sessions hash table, to allow fast sid to session retrieval */ @@ -674,6 +675,46 @@ return 0; } +/* For the messages module */ +int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new) +{ + TRACE_ENTRY("%p %zd %p %p", sid, len, session, new); + CHECK_PARAMS( sid && len && session && VALIDATE_SI(*session) ); + + /* Get the session object */ + CHECK_FCT( fd_sess_fromsid ( (char *) sid, len, session, new) ); + + /* Update the msg refcount */ + CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) ); + (*session)->msg_cnt++; + CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) ); + + /* Done */ + return 0; +} + +int fd_sess_reclaim_msg ( struct session ** session ) +{ + int reclaim; + + TRACE_ENTRY("%p", session); + CHECK_PARAMS( session && VALIDATE_SI(*session) ); + + /* Update the msg refcount */ + CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) ); + reclaim = (*session)->msg_cnt; + (*session)->msg_cnt = reclaim - 1; + CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) ); + + if (reclaim == 1) { + CHECK_FCT(fd_sess_reclaim ( session )); + } else { + *session = NULL; + } + return 0; +} + + /* Dump functions */ void fd_sess_dump(int level, struct session * session)