changeset 78:a58f0757c06a

Added code for DPR/DPA
author Sebastien Decugis <sdecugis@nict.go.jp>
date Tue, 01 Dec 2009 16:24:06 +0900
parents 33d8bed6a9d7
children d273a2ce19c8
files freeDiameter/fD.h freeDiameter/messages.c freeDiameter/p_ce.c freeDiameter/p_cnx.c freeDiameter/p_dp.c freeDiameter/p_expiry.c freeDiameter/p_psm.c freeDiameter/peers.c include/freeDiameter/freeDiameter.h
diffstat 9 files changed, 132 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/freeDiameter/fD.h	Tue Dec 01 14:30:29 2009 +0900
+++ b/freeDiameter/fD.h	Tue Dec 01 16:24:06 2009 +0900
@@ -90,6 +90,7 @@
 struct dict_object * fd_dict_avp_OSI; /* Origin-State-Id */
 struct dict_object * fd_dict_cmd_CER; /* Capabilities-Exchange-Request */
 struct dict_object * fd_dict_cmd_DWR; /* Device-Watchdog-Request */
+struct dict_object * fd_dict_avp_DC;  /* Disconnect-Cause */
 struct dict_object * fd_dict_cmd_DPR; /* Disconnect-Peer-Request */
 
 /* Global message queues */
@@ -256,7 +257,7 @@
 /* Peer state machine */
 int  fd_psm_start();
 int  fd_psm_begin(struct fd_peer * peer );
-int  fd_psm_terminate(struct fd_peer * peer );
+int  fd_psm_terminate(struct fd_peer * peer, char * reason );
 void fd_psm_abord(struct fd_peer * peer );
 void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay);
 int fd_psm_change_state(struct fd_peer * peer, int new_state);
@@ -286,7 +287,7 @@
 int fd_p_dw_timeout(struct fd_peer * peer);
 int fd_p_dw_reopen(struct fd_peer * peer);
 int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer);
-int fd_p_dp_initiate(struct fd_peer * peer);
+int fd_p_dp_initiate(struct fd_peer * peer, char * reason);
 
 /* Active peers -- routing process should only ever take the read lock, the write lock is managed by PSMs */
 extern struct fd_list fd_g_activ_peers;
--- a/freeDiameter/messages.c	Tue Dec 01 14:30:29 2009 +0900
+++ b/freeDiameter/messages.c	Tue Dec 01 16:24:06 2009 +0900
@@ -44,6 +44,7 @@
 struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
 struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
 struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
+struct dict_object * fd_dict_avp_DC  = NULL; /* Disconnect-Cause */
 struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */
 
 /* Resolve the dictionary objects */
@@ -61,6 +62,8 @@
 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT)  );
 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP",      	&dict_avp_FAVP, ENOENT)  );
 	
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", 	&fd_dict_avp_DC , ENOENT)  );
+	
 	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) );
 	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) );
 	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) );
--- a/freeDiameter/p_ce.c	Tue Dec 01 14:30:29 2009 +0900
+++ b/freeDiameter/p_ce.c	Tue Dec 01 16:24:06 2009 +0900
@@ -762,7 +762,7 @@
 			CHECK_FCT_DO( (*peer->p_cb2)( &peer->p_hdr.info ),
 				{
 					TRACE_DEBUG(INFO, "Validation callback rejected the peer %s after handshake", peer->p_hdr.info.pi_diamid);
-					CHECK_FCT( fd_psm_terminate( peer ) );
+					CHECK_FCT( fd_psm_terminate( peer, "DO_NOT_WANT_TO_TALK_TO_YOU" ) );
 					return 0;
 				}  );
 		}
--- a/freeDiameter/p_cnx.c	Tue Dec 01 14:30:29 2009 +0900
+++ b/freeDiameter/p_cnx.c	Tue Dec 01 16:24:06 2009 +0900
@@ -87,7 +87,7 @@
 		ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai);
 		if (ret) {
 			fd_log_debug("Unable to resolve address for peer '%s' (%s), aborting\n", peer->p_hdr.info.pi_diamid, gai_strerror(ret));
-			fd_psm_terminate( peer );
+			fd_psm_terminate( peer, NULL );
 			return 0;
 		}
 		
@@ -109,7 +109,7 @@
 	/* Now check we have at least one address to attempt */
 	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
 		fd_log_debug("No address %savailable to connect to peer '%s', aborting\n", peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid);
-		fd_psm_terminate( peer );
+		fd_psm_terminate( peer, NULL );
 		return 0;
 	}
 	
--- a/freeDiameter/p_dp.c	Tue Dec 01 14:30:29 2009 +0900
+++ b/freeDiameter/p_dp.c	Tue Dec 01 16:24:06 2009 +0900
@@ -40,18 +40,116 @@
 /* Handle a received message */
 int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer)
 {
-	TODO("Handle depending on DPR or DPA and peer state");
+	int delay = 0;
+	TRACE_ENTRY("%p %d %p", msg, req, peer);
 	
-	return ENOTSUP;
+	if (req) {
+		/* We received a DPR, save the Disconnect-Cause and terminate the connection */
+		struct avp * dc;
+		
+		CHECK_FCT_DO( fd_msg_search_avp ( *msg, fd_dict_avp_DC, &dc ), return );
+		if (dc) {
+			/* Check the value is consistent with the saved one */
+			struct avp_hdr * hdr;
+			CHECK_FCT_DO(  fd_msg_avp_hdr( dc, &hdr ), return  );
+			if (hdr->avp_value == NULL) {
+				/* This is a sanity check */
+				TRACE_DEBUG(NONE, "BUG: Unset value in Disconnect-Cause in DPR");
+				fd_msg_dump_one(NONE, dc);
+				ASSERT(0); /* To check if this really happens, and understand why... */
+			}
+
+			peer->p_hdr.info.runtime.pir_lastDC = hdr->avp_value->u32;
+		}
+		if (TRACE_BOOL(INFO)) {
+			if (dc) {
+				struct dict_object * dictobj = NULL;
+				struct dict_enumval_request er;
+				memset(&er, 0, sizeof(er));
+				CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT )  );
+				er.search.enum_value.u32 = peer->p_hdr.info.runtime.pir_lastDC;
+				CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, 0 )  );
+				if (dictobj) {
+					CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
+					fd_log_debug("Peer '%s' sent a DPR with cause: %s\n", peer->p_hdr.info.pi_diamid, er.search.enum_name);
+				} else {
+					fd_log_debug("Peer '%s' sent a DPR with unknown cause: %u\n", peer->p_hdr.info.pi_diamid, peer->p_hdr.info.runtime.pir_lastDC);
+				}
+			} else {
+				fd_log_debug("Peer '%s' sent a DPR without Disconnect-Cause AVP\n", peer->p_hdr.info.pi_diamid);
+			}
+		}
+		
+		/* Now reply with a DPA */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
+		CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
+		CHECK_FCT( fd_msg_add_origin ( *msg, 0 ) );
+		
+		/* Move to CLOSING state to failover outgoing messages (and avoid failing the DPA...) */
+		CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
+		
+		/* Now send the DPA */
+		CHECK_FCT( fd_out_send( msg, NULL, peer) );
+		
+	} else {
+		/* We received a DPA */
+		if (peer->p_hdr.info.runtime.pir_state != STATE_CLOSING) {
+			TRACE_DEBUG(INFO, "Ignore DPA received in state %s", STATE_STR(peer->p_hdr.info.runtime.pir_state));
+		}
+			
+	}
+	
+	if (*msg) {
+		/* In theory, we should control the Result-Code AVP. But since we will not go back to OPEN state here, let's skip it */
+		CHECK_FCT_DO( fd_msg_free( *msg ), /* continue */ );
+		*msg = NULL;
+	}
+	
+	/* The calling function handles cleaning the PSM and terminating the peer */
+	return 0;
 }
 
 /* Start disconnection of a peer: send DPR */
-int fd_p_dp_initiate(struct fd_peer * peer)
+int fd_p_dp_initiate(struct fd_peer * peer, char * reason)
 {
-	TODO("Create the DPR message");
-	TODO("Send it");
-	TODO("Mark the peer as CLOSING");
-	TODO("Reset the timer");
+	struct msg * msg = NULL;
+	struct dict_object * dictobj = NULL;
+	struct avp * avp = NULL;
+	struct dict_enumval_request er;
+	union avp_value val;
+	
+	TRACE_ENTRY("%p %p", peer, reason);
+	
+	/* Create a new DWR instance */
+	CHECK_FCT( fd_msg_new ( fd_dict_cmd_DPR, MSGFL_ALLOC_ETEID, &msg ) );
+	
+	/* Add the Origin information */
+	CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
+	
+	/* Add the Disconnect-Cause */
+	CHECK_FCT( fd_msg_avp_new ( fd_dict_avp_DC, 0, &avp ) );
 	
-	return ENOTSUP;
+	/* Search the value in the dictionary */
+	memset(&er, 0, sizeof(er));
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT )  );
+	er.search.enum_name = reason ?: "REBOOTING";
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT )  );
+	CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
+	
+	/* Set the value in the AVP */
+	val.u32 = er.search.enum_value.u32;
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	/* Save the value also in the peer */
+	peer->p_hdr.info.runtime.pir_lastDC = val.u32;
+	
+	/* Now send this message */
+	CHECK_FCT( fd_out_send(&msg, NULL, peer) );
+	
+	/* Update the peer state and timer */
+	CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
+	fd_psm_next_timeout(peer, 0, DPR_TIMEOUT);
+	
+	return 0;
 }
--- a/freeDiameter/p_expiry.c	Tue Dec 01 14:30:29 2009 +0900
+++ b/freeDiameter/p_expiry.c	Tue Dec 01 16:24:06 2009 +0900
@@ -131,7 +131,7 @@
 		
 		/* Now, the first peer in the list is expired; signal it */
 		fd_list_unlink( &first->p_expiry );
-		CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, 0, NULL), goto error );
+		CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, 0, "DO_NOT_WANT_TO_TALK_TO_YOU"), goto error );
 		
 	} while (1);
 	
--- a/freeDiameter/p_psm.c	Tue Dec 01 14:30:29 2009 +0900
+++ b/freeDiameter/p_psm.c	Tue Dec 01 16:24:06 2009 +0900
@@ -93,9 +93,8 @@
 	if (peer->p_cb2) {
 		CHECK_FCT_DO( (*peer->p_cb2)(&peer->p_hdr.info),
 			{
-				TRACE_DEBUG(FULL, "Validation failed, moving to state CLOSING");
-				peer->p_hdr.info.runtime.pir_state = STATE_CLOSING;
-				fd_psm_terminate(peer);
+				TRACE_DEBUG(FULL, "Validation failed, terminating the connection");
+				fd_psm_terminate(peer, "DO_NOT_WANT_TO_TALK_TO_YOU" );
 			} );
 		peer->p_cb2 = NULL;
 		return 0;
@@ -160,6 +159,10 @@
 			}
 			break;
 			
+			case FDEVP_TERMINATE:
+				/* Do not free the string since it is a constant */
+			break;
+			
 			case FDEVP_CNX_INCOMING: {
 				struct cnx_incoming * evd = ev->data;
 				CHECK_FCT_DO( fd_msg_free(evd->cer), /* continue */);
@@ -215,6 +218,8 @@
 /* Set timeout timer of next event */
 void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay)
 {
+	TRACE_DEBUG(FULL, "Peer timeout reset to %d seconds%s", delay, add_random ? " (+/- 2)" : "" );
+	
 	/* Initialize the timer */
 	CHECK_POSIX_DO(  clock_gettime( CLOCK_REALTIME,  &peer->p_psm_timer ), ASSERT(0) );
 	
@@ -235,8 +240,6 @@
 	
 	peer->p_psm_timer.tv_sec += delay;
 	
-	TRACE_DEBUG(FULL, "Peer timeout reset to %d seconds%s", delay, add_random ? " (+/- 2)" : "" );
-	
 #ifdef SLOW_PSM
 	/* temporary for debug */
 	peer->p_psm_timer.tv_sec += 10;
@@ -347,7 +350,7 @@
 			case STATE_OPEN:
 			case STATE_REOPEN:
 				/* We cannot just close the conenction, we have to send a DPR first */
-				CHECK_FCT_DO( fd_p_dp_initiate(peer), goto psm_end );
+				CHECK_FCT_DO( fd_p_dp_initiate(peer, ev_data), goto psm_end );
 				goto psm_loop;
 			
 			/*	
@@ -470,6 +473,8 @@
 			
 			case CC_DISCONNECT_PEER:
 				CHECK_FCT_DO( fd_p_dp_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_end );
+				if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSING)
+					goto psm_end;
 				break;
 			
 			case CC_DEVICE_WATCHDOG:
@@ -711,13 +716,13 @@
 }
 
 /* End the PSM (clean ending) */
-int fd_psm_terminate(struct fd_peer * peer )
+int fd_psm_terminate(struct fd_peer * peer, char * reason )
 {
 	TRACE_ENTRY("%p", peer);
 	CHECK_PARAMS( CHECK_PEER(peer) );
 	
 	if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
-		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
+		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, reason) );
 	} else {
 		TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
 	}
--- a/freeDiameter/peers.c	Tue Dec 01 14:30:29 2009 +0900
+++ b/freeDiameter/peers.c	Tue Dec 01 16:24:06 2009 +0900
@@ -254,7 +254,7 @@
 		struct fd_peer * peer = (struct fd_peer *)li;
 		
 		if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
-			CHECK_FCT_DO( fd_psm_terminate(peer), /* continue */ );
+			CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ );
 		} else {
 			li = li->prev; /* to avoid breaking the loop */
 			fd_list_unlink(&peer->p_hdr.chain);
--- a/include/freeDiameter/freeDiameter.h	Tue Dec 01 14:30:29 2009 +0900
+++ b/include/freeDiameter/freeDiameter.h	Tue Dec 01 16:24:06 2009 +0900
@@ -337,6 +337,8 @@
 		struct fd_list	pir_apps;	/* applications advertised by the remote peer, except relay (pi_flags.relay) */
 		int		pir_isi;	/* Inband-Security-Id advertised (PI_SEC_* bits) */
 		
+		uint32_t	pir_lastDC;	/* The last Disconnect-Cause value received */
+		
 		int		pir_proto;	/* The L4 protocol currently used with the peer (IPPROTO_TCP or IPPROTO_SCTP) */
 		const gnutls_datum_t 	*pir_cert_list; 	/* The (valid) credentials that the peer has presented, or NULL if TLS is not used */
 								/* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo 
"Welcome to our mercurial repository"