changeset 85:e5fcd672caff

Added new function to retrieve messages sessions easily
author Sebastien Decugis <sdecugis@nict.go.jp>
date Thu, 03 Dec 2009 14:59:23 +0900
parents 03740d31e56e
children e3e22d89e023
files freeDiameter/routing.c include/freeDiameter/freeDiameter.h include/freeDiameter/libfreeDiameter.h libfreeDiameter/libfD.h libfreeDiameter/messages.c libfreeDiameter/sessions.c
diffstat 6 files changed, 277 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- 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 );
--- 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 );
 
 
 /***************************************/
--- 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                 */
 /***************************************/
--- 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 */
--- 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;
--- 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)
"Welcome to our mercurial repository"