Commit 50af4691 authored by Sebastien Decugis's avatar Sebastien Decugis
Browse files

Added new function to retrieve messages sessions easily

parent 7990a429
......@@ -51,3 +51,9 @@ int fd_rt_fini(void)
{
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 );
......@@ -444,35 +444,172 @@ int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor,
* - 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.
*
* 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.
* 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
*
* 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 );
/***************************************/
......
......@@ -1472,8 +1472,7 @@ int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t o
* 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 @@ struct rtd_candidate {
/* 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 @@ int fd_msg_source_get( struct msg * msg, char ** diamid );
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 */
/***************************************/
......
......@@ -55,4 +55,8 @@ int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp
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 */
......@@ -118,6 +118,7 @@ struct msg {
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 @@ static int destroy_obj (struct msg_avp_chain * obj )
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 @@ public:
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 @@ int fd_msg_source_get( struct msg * msg, char ** diamid )
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;
......
......@@ -99,6 +99,7 @@ struct session {
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 @@ int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct s
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)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment