diff freeDiameter/p_psm.c @ 37:cc3c59fe98fe

Lot of cleanups in peer structure management
author Sebastien Decugis <sdecugis@nict.go.jp>
date Thu, 05 Nov 2009 14:28:46 +0900
parents 1498b3c7304c
children 68c1890f7049
line wrap: on
line diff
--- a/freeDiameter/p_psm.c	Mon Nov 02 17:31:36 2009 +0900
+++ b/freeDiameter/p_psm.c	Thu Nov 05 14:28:46 2009 +0900
@@ -94,7 +94,7 @@
 		CHECK_FCT_DO( (*peer->p_cb2)(&peer->p_hdr.info),
 			{
 				TRACE_DEBUG(FULL, "Validation failed, moving to state CLOSING");
-				peer->p_hdr.info.pi_state = STATE_CLOSING;
+				peer->p_hdr.info.runtime.pir_state = STATE_CLOSING;
 				fd_psm_terminate(peer);
 			} );
 		peer->p_cb2 = NULL;
@@ -122,6 +122,9 @@
 	/* Start the thread to handle outgoing messages */
 	CHECK_FCT( fd_out_start(peer) );
 	
+	/* Update the expiry timer now */
+	CHECK_FCT( fd_p_expi_update(peer) );
+	
 	return 0;
 }
 static int leave_open_state(struct fd_peer * peer)
@@ -144,6 +147,32 @@
 /************************************************************************/
 /*                      Helpers for state changes                       */
 /************************************************************************/
+
+/* Cleanup pending events in the peer */
+void fd_psm_events_free(struct fd_peer * peer)
+{
+	struct fd_event * ev;
+	/* Purge all events, and free the associated data if any */
+	while (fd_fifo_tryget( peer->p_events, &ev ) == 0) {
+		switch (ev->code) {
+			case FDEVP_CNX_ESTABLISHED: {
+				fd_cnx_destroy(ev->data);
+			}
+			break;
+			
+			case FDEVP_CNX_INCOMING: {
+				struct cnx_incoming * evd = ev->data;
+				CHECK_FCT_DO( fd_msg_free(evd->cer), /* continue */);
+				fd_cnx_destroy(evd->cnx);
+			}
+			default:
+				free(ev->data);
+		}
+		free(ev);
+	}
+}
+
+
 /* Change state */
 int fd_psm_change_state(struct fd_peer * peer, int new_state)
 {
@@ -151,7 +180,7 @@
 	
 	TRACE_ENTRY("%p %d(%s)", peer, new_state, STATE_STR(new_state));
 	CHECK_PARAMS( CHECK_PEER(peer) );
-	old = peer->p_hdr.info.pi_state;
+	old = peer->p_hdr.info.runtime.pir_state;
 	if (old == new_state)
 		return 0;
 	
@@ -164,14 +193,20 @@
 		CHECK_FCT( leave_open_state(peer) );
 	}
 	
-	peer->p_hdr.info.pi_state = new_state;
+	peer->p_hdr.info.runtime.pir_state = new_state;
 	
 	if (new_state == STATE_OPEN) {
 		CHECK_FCT( enter_open_state(peer) );
 	}
 	
-	if ((new_state == STATE_CLOSED) && (peer->p_hdr.info.pi_flags.persist == PI_PRST_NONE)) {
-		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
+	if (new_state == STATE_CLOSED) {
+		/* Purge event list */
+		fd_psm_events_free(peer);
+		
+		/* If the peer is not persistant, we destroy it */
+		if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_NONE) {
+			CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
+		}
 	}
 	
 	return 0;
@@ -209,17 +244,23 @@
 /* Cleanup the peer */
 void fd_psm_cleanup(struct fd_peer * peer)
 {
-	/* Move to CLOSED state */
+	/* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */
 	CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ );
 	
-	/* Destroy the connection */
+	/* Destroy data */
+	CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */);
 	if (peer->p_cnxctx) {
 		fd_cnx_destroy(peer->p_cnxctx);
 		peer->p_cnxctx = NULL;
 	}
-	
-	/* What else ? */
-	TODO("...");
+	if (peer->p_initiator) {
+		fd_cnx_destroy(peer->p_initiator);
+		peer->p_initiator = NULL;
+	}
+	if (peer->p_receiver) {
+		fd_cnx_destroy(peer->p_receiver);
+		peer->p_receiver = NULL;
+	}
 	
 }
 
@@ -232,7 +273,7 @@
 {
 	struct fd_peer * peer = (struct fd_peer *)arg;
 	CHECK_PARAMS_DO( CHECK_PEER(peer), return );
-	peer->p_hdr.info.pi_state = STATE_ZOMBIE;
+	peer->p_hdr.info.runtime.pir_state = STATE_ZOMBIE;
 	return;
 }
 
@@ -257,7 +298,7 @@
 	}
 	
 	/* The state machine starts in CLOSED state */
-	peer->p_hdr.info.pi_state = STATE_CLOSED;
+	peer->p_hdr.info.runtime.pir_state = STATE_CLOSED;
 	
 	/* Wait that the PSM are authorized to start in the daemon */
 	CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end );
@@ -273,16 +314,16 @@
 	/* Get next event */
 	CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end );
 	TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'",
-			STATE_STR(peer->p_hdr.info.pi_state),
+			STATE_STR(peer->p_hdr.info.runtime.pir_state),
 			fd_pev_str(event), ev_data, ev_sz,
 			peer->p_hdr.info.pi_diamid);
 
 	/* Now, the action depends on the current state and the incoming event */
 
 	/* The following states are impossible */
-	ASSERT( peer->p_hdr.info.pi_state != STATE_NEW );
-	ASSERT( peer->p_hdr.info.pi_state != STATE_ZOMBIE );
-	ASSERT( peer->p_hdr.info.pi_state != STATE_OPEN_HANDSHAKE ); /* because it exists only between two loops */
+	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_NEW );
+	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE );
+	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_OPEN_HANDSHAKE ); /* because it exists only between two loops */
 
 	/* Purge invalid events */
 	if (!CHECK_PEVENT(event)) {
@@ -298,7 +339,7 @@
 
 	/* Requests to terminate the peer object */
 	if (event == FDEVP_TERMINATE) {
-		switch (peer->p_hdr.info.pi_state) {
+		switch (peer->p_hdr.info.runtime.pir_state) {
 			case STATE_OPEN:
 			case STATE_REOPEN:
 				/* We cannot just close the conenction, we have to send a DPR first */
@@ -324,6 +365,13 @@
 		struct msg * msg = NULL;
 		struct msg_hdr * hdr;
 		
+		/* If the current state does not allow receiving messages, just drop it */
+		if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSED) {
+			TRACE_DEBUG(FULL, "Purging message in queue while in CLOSED state (%zdb)", ev_sz);
+			free(ev_data);
+			goto psm_loop;
+		}
+		
 		/* Parse the received buffer */
 		CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg), 
 			{
@@ -338,13 +386,13 @@
 		/* Extract the header */
 		CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end );
 		
-		/* If it is an answer, associate with the request */
+		/* If it is an answer, associate with the request or drop */
 		if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
 			struct msg * req;
 			/* Search matching request (same hbhid) */
 			CHECK_FCT_DO( fd_p_sr_fetch(&peer->p_sr, hdr->msg_hbhid, &req), goto psm_end );
 			if (req == NULL) {
-				fd_log_debug("Received a Diameter answer message with no corresponding sent request, discarding...\n");
+				fd_log_debug("Received a Diameter answer message with no corresponding sent request, discarding.\n");
 				fd_msg_dump_walk(NONE, msg);
 				fd_msg_free(msg);
 				goto psm_loop;
@@ -356,30 +404,44 @@
 		
 		/* Now handle non-link-local messages */
 		if (fd_msg_is_routable(msg)) {
-			/* If we are not in OPEN state, discard the message */
-			if (peer->p_hdr.info.pi_state != STATE_OPEN) {
-				fd_log_debug("Received a routable message while not in OPEN state from peer '%s', discarded.\n", peer->p_hdr.info.pi_diamid);
-				fd_msg_dump_walk(NONE, msg);
-				fd_msg_free(msg);
-			} else {
-				/* We received a valid message, update the expiry timer */
-				CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end );
+			switch (peer->p_hdr.info.runtime.pir_state) {
+				/* To maximize compatibility -- should not be a security issue here */
+				case STATE_REOPEN:
+				case STATE_SUSPECT:
+				case STATE_CLOSING:
+					TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state");
+				/* The standard situation : */
+				case STATE_OPEN:
+					/* We received a valid message, update the expiry timer */
+					CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end );
+
+					/* Set the message source and add the Route-Record */
+					CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, 1, fd_g_config->cnf_dict ), goto psm_end);
+
+					/* Requeue to the global incoming queue */
+					CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end );
 
-				/* Set the message source and add the Route-Record */
-				CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, 1, fd_g_config->cnf_dict ), goto psm_end);
-
-				/* Requeue to the global incoming queue */
-				CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end );
-				
-				/* Update the peer timer */
-				if (!peer->p_flags.pf_dw_pending) {
-					fd_psm_next_timeout(peer, 1, peer->p_hdr.info.pi_twtimer ?: fd_g_config->cnf_timer_tw);
-				}
+					/* Update the peer timer (only in OPEN state) */
+					if ((peer->p_hdr.info.runtime.pir_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) {
+						fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+					}
+					break;
+					
+				/* In other states, we discard the message, it is either old or invalid to send it for the remote peer */
+				case STATE_WAITCNXACK:
+				case STATE_WAITCNXACK_ELEC:
+				case STATE_WAITCEA:
+				case STATE_CLOSED:
+				default:
+					/* In such case, just discard the message */
+					fd_log_debug("Received a routable message while not in OPEN state from peer '%s', discarded.\n", peer->p_hdr.info.pi_diamid);
+					fd_msg_dump_walk(NONE, msg);
+					fd_msg_free(msg);
 			}
 			goto psm_loop;
 		}
 		
-		/* Link-local message: They must be understood by our dictionary */
+		/* Link-local message: They must be understood by our dictionary, otherwise we return an error */
 		{
 			int ret;
 			CHECK_FCT_DO( ret = fd_msg_parse_or_error( &msg ),
@@ -395,25 +457,45 @@
 				} );
 		}
 		
-		ASSERT( hdr->msg_appl == 0 ); /* buggy fd_msg_is_routable() ? */
-		
 		/* Handle the LL message and update the expiry timer appropriately */
 		switch (hdr->msg_code) {
-			case CC_DEVICE_WATCHDOG:
-				CHECK_FCT_DO( fd_p_dw_handle(&msg, peer), goto psm_end );
+			case CC_CAPABILITIES_EXCHANGE:
+				CHECK_FCT_DO( fd_p_ce_handle(&msg, peer), goto psm_end );
 				break;
 			
 			case CC_DISCONNECT_PEER:
 				CHECK_FCT_DO( fd_p_dp_handle(&msg, peer), goto psm_end );
 				break;
 			
-			case CC_CAPABILITIES_EXCHANGE:
-				CHECK_FCT_DO( fd_p_ce_handle(&msg, peer), goto psm_end );
+			case CC_DEVICE_WATCHDOG:
+				CHECK_FCT_DO( fd_p_dw_handle(&msg, peer), goto psm_end );
 				break;
 			
 			default:
 				/* Unknown / unexpected / invalid message */
-				TODO("Log, return error message if request");
+				fd_log_debug("Received an unknown local message from peer '%s', discarded.\n", peer->p_hdr.info.pi_diamid);
+				fd_msg_dump_walk(NONE, msg);
+				if (hdr->msg_flags & CMD_FLAG_REQUEST) {
+					do {
+						/* Reply with an error code */
+						CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ), break );
+
+						/* Set the error code */
+						CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_INVALID_HDR_BITS", NULL, NULL, 1 ), break );
+
+						/* Send the answer */
+						CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer), break );
+					} while (0);
+				} else {
+					/* We did ASK for it ??? */
+					fd_log_debug("Invalid PXY flag in header ?\n");
+				}
+				
+				/* Cleanup the message if not done */
+				if (msg) {
+					CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
+					msg = NULL;
+				}
 		};
 		
 		/* At this point the message must have been fully handled already */
@@ -428,24 +510,37 @@
 	
 	/* The connection object is broken */
 	if (event == FDEVP_CNX_ERROR) {
-		/* Cleanup the peer */
-		fd_psm_cleanup(peer);
-		
-		/* Mark the connection problem */
-		peer->p_flags.pf_cnx_pb = 1;
-		
-		/* Move to CLOSED */
-		CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), goto psm_end );
-		
-		/* Reset the timer */
-		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.pi_tctimer ?: fd_g_config->cnf_timer_tc);
-		
-		/* Loop */
-		goto psm_loop;
+		switch (peer->p_hdr.info.runtime.pir_state) {
+			case STATE_WAITCNXACK_ELEC:
+				TODO("Reply CEA on the receiver side and go to OPEN state");
+				goto psm_loop;
+			
+			case STATE_OPEN:
+			case STATE_REOPEN:
+			case STATE_WAITCNXACK:
+			case STATE_WAITCEA:
+			case STATE_SUSPECT:
+			default:
+				/* Mark the connection problem */
+				peer->p_flags.pf_cnx_pb = 1;
+				
+			case STATE_CLOSING:
+				/* Cleanup the peer */
+				fd_psm_cleanup(peer);
+
+				/* Reset the timer */
+				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
+				
+			case STATE_CLOSED:
+				/* Go to the next event */
+				goto psm_loop;
+		}
 	}
 	
 	/* The connection notified a change in endpoints */
 	if (event == FDEVP_CNX_EP_CHANGE) {
+		/* We actually don't care if we are in OPEN state here... */
+		
 		/* Cleanup the remote LL and primary addresses */
 		CHECK_FCT_DO( fd_ep_filter( &peer->p_hdr.info.pi_endpoints, EP_FL_CONF | EP_FL_DISC | EP_FL_ADV ), /* ignore the error */);
 		CHECK_FCT_DO( fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_PRIMARY ), /* ignore the error */);
@@ -453,8 +548,10 @@
 		/* Get the new ones */
 		CHECK_FCT_DO( fd_cnx_getendpoints(peer->p_cnxctx, NULL, &peer->p_hdr.info.pi_endpoints), /* ignore the error */);
 		
+		/* We do not support local endpoints change currently, but it could be added here if needed */
+		
 		if (TRACE_BOOL(ANNOYING)) {
-			fd_log_debug("New remote endpoint(s):\n");
+			TRACE_DEBUG(ANNOYING, "New remote endpoint(s):" );
 			fd_ep_dump(6, &peer->p_hdr.info.pi_endpoints);
 		}
 		
@@ -487,14 +584,16 @@
 	
 	/* The timeout for the current state has been reached */
 	if (event == FDEVP_PSM_TIMEOUT) {
-		switch (peer->p_hdr.info.pi_state) {
+		switch (peer->p_hdr.info.runtime.pir_state) {
 			case STATE_OPEN:
 			case STATE_REOPEN:
 				CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end );
 				break;
 				
 			case STATE_CLOSED:
-				TODO("Initiate a new connection");
+				CHECK_FCT_DO( fd_psm_change_state(peer, STATE_WAITCNXACK), goto psm_end );
+				fd_psm_next_timeout(peer, 0, CNX_TIMEOUT);
+				CHECK_FCT_DO( fd_p_cnx_init(peer), goto psm_end );
 				break;
 				
 			case STATE_CLOSING:
@@ -503,8 +602,7 @@
 			case STATE_WAITCEA:
 				/* Destroy the connection, restart the timer to a new connection attempt */
 				fd_psm_cleanup(peer);
-				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.pi_tctimer ?: fd_g_config->cnf_timer_tc);
-				CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), goto psm_end );
+				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
 				break;
 				
 			case STATE_WAITCNXACK_ELEC:
@@ -514,7 +612,7 @@
 	}
 	
 	/* Default action : the handling has not yet been implemented. [for debug only] */
-	TODO("Missing handler in PSM : '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.pi_state), fd_pev_str(event));
+	TODO("Missing handler in PSM : '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.runtime.pir_state), fd_pev_str(event));
 	if (event == FDEVP_PSM_TIMEOUT) {
 		/* We have not handled timeout in this state, let's postpone next alert */
 		fd_psm_next_timeout(peer, 0, 60);
@@ -524,6 +622,7 @@
 
 psm_end:
 	fd_psm_cleanup(peer);
+	CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
 	pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
 	peer->p_psm = (pthread_t)NULL;
 	pthread_detach(pthread_self());
@@ -540,7 +639,10 @@
 	TRACE_ENTRY("%p", peer);
 	
 	/* Check the peer and state are OK */
-	CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.pi_state == STATE_NEW) );
+	CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.runtime.pir_state == STATE_NEW) );
+	
+	/* Create the FIFO for events */
+	CHECK_FCT( fd_fifo_new(&peer->p_events) );
 	
 	/* Create the PSM controler thread */
 	CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) );
@@ -555,7 +657,7 @@
 	TRACE_ENTRY("%p", peer);
 	CHECK_PARAMS( CHECK_PEER(peer) );
 	
-	if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
+	if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
 		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
 	} else {
 		TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
@@ -571,21 +673,13 @@
 	/* Cancel PSM thread */
 	CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ );
 	
-	/* Cancel the OUT thread */
-	CHECK_FCT_DO( fd_out_stop(peer), /* continue */ );
-	
-	/* Cleanup the connection */
-	if (peer->p_cnxctx) {
-		fd_cnx_destroy(peer->p_cnxctx);
-	}
+	/* Cleanup the data */
+	fd_psm_cleanup(peer);
 	
-	/* Failover the messages */
-	fd_peer_failover_msg(peer);
+	/* Destroy the event list */
+	CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
 	
-	/* Empty the events list, this might leak some memory, but we only do it on exit, so... */
-	fd_event_destroy(&peer->p_events, free);
-	
-	/* More cleanups are performed in fd_peer_free */
+	/* Remaining cleanups are performed in fd_peer_free */
 	return;
 }
 
"Welcome to our mercurial repository"