changeset 172:b70c4a99babc

Improvements on the peer module, TBC
author Sebastien Decugis <sdecugis@nict.go.jp>
date Mon, 29 Sep 2008 16:13:56 +0900
parents e11140e4869f
children eb3b056ca409
files extensions/sec_nosec/sec_nosec.h include/waaad/peer-api.h include/waaad/security-api.h waaad/Makefile.am waaad/log.h waaad/peer-internal.h waaad/peer-listener.c waaad/peer-psm.c waaad/peer-sec-ini.c waaad/peer.c waaad/tests/Makefile.am waaad/utils.h
diffstat 12 files changed, 685 insertions(+), 213 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/sec_nosec/sec_nosec.h	Thu Sep 18 18:28:58 2008 +0900
+++ b/extensions/sec_nosec/sec_nosec.h	Mon Sep 29 16:13:56 2008 +0900
@@ -59,8 +59,9 @@
 #define SEC_NOSEC_INBAND_SECURITY_ID	0
 
 /* The maximum size of messages we accept. Bigger messages are considered as trash and failure occurs */
+#ifndef DIAMETER_MSG_SIZE_MAX
 #define DIAMETER_MSG_SIZE_MAX	65536
-
+#endif /* DIAMETER_MSG_SIZE_MAX */
 
 
 /**************************************************************************
--- a/include/waaad/peer-api.h	Thu Sep 18 18:28:58 2008 +0900
+++ b/include/waaad/peer-api.h	Mon Sep 29 16:13:56 2008 +0900
@@ -79,7 +79,7 @@
 typedef struct _pa_ {
 	
 	/* Flags: */
-#define PA_NOSEC	1<<0	/* disable completly the security ? -- SHOULD BE DISCOURAGED!!! */
+#define PA_NOSEC	1<<0	/* disable completly the security ? -- SHOULD BE DEPRECATED!!! */
 #define PA_EXPIRY	1<<1	/* for peers dynamically discovered, set this flag and the lifetime in pa_ts (DNS TTL, ...). if pa_ts is 0, the peer expires at transport disconnection */
 #define PA_CB		1<<2	/* the pa_cb callback must be called with the pa_cbparm as parameter when peer expires (see pa_cb definition) */
 #define PA_FREE		1<<3	/* when peer is destroyed, free() must be called on the pa_cbparm */
--- a/include/waaad/security-api.h	Thu Sep 18 18:28:58 2008 +0900
+++ b/include/waaad/security-api.h	Mon Sep 29 16:13:56 2008 +0900
@@ -128,7 +128,6 @@
 /* The following enum represents the possible states of a peer with regards to the security module (PSS = peer security state) */
 typedef enum {
 	PSS_CLOSED = 1,		/* The peer is not connected */
-	PSS_INITIAL,		/* The connection is in progress (CER/CEA, election, ...). */
 	PSS_CONNECTED,		/* The connection is established, messages MUST be protected in this state */
 	PSS_CLOSING		/* The connection is being shutdown, the security extension must proceed to the cleanup of the session */
 } sec_pss_t;
@@ -137,8 +136,9 @@
 typedef struct _sec_session {
 	peer_t			*peer;		/* Reference to the peer to which this session applies */
 	
-	/* The next 3 fields are valid only in the PSS_INITIAL and PSS_CONNECTED states */
+	/* The following fields are not valid in PSS_CLOSED state */
 	sec_conn_t		*conn;		/* The connection for sending/receiving to/from this peer */
+	int			 side;		/* 1: this side is initiator (=> client); 2: this side is responder (=> server) */
 	int			 proto;		/* IPPROTO_TCP or IPPROTO_SCTP */
 	union {
 		struct {
@@ -169,15 +169,12 @@
  *  The old and new states are provided.
  *  Notes: 
  *   Valid transitions are:
- *    PSS_CLOSED -> PSS_INITIAL		( A new connection has been established )
- *    PSS_INITIAL -> PSS_CONNECTED	( The CER / CEA exchange is successful )
- *    PSS_INITIAL -> PSS_CLOSING	( The CER / CEA exchange failed, the connection is terminating )
- *    PSS_INITIAL -> PSS_CLOSED		( The connection has closed unexpectedly )
+ *    PSS_CLOSED -> PSS_CONNECTED	( The CER / CEA exchange is successful )
  *    PSS_CONNECTED -> PSS_CLOSING	( The diameter exchange is terminated, connection is terminating )
+ *    PSS_CLOSING -> PSS_CLOSED		( The connection has been closed properly. The ext_session must be freed if not already )
  *    PSS_CONNECTED -> PSS_CLOSED       ( An unexpected error occurred, the transport-layer connection is not available for clean shutdown )
- *    PSS_CLOSING -> PSS_CLOSED		( The connection has been closed properly. The ext_session must be freed if not already )
  *
- *   The socket and send/receive functions are usable only in the INITIAL, CONNECTED and CLOSING states.
+ *   The socket and send/receive functions are usable only in the CONNECTED and CLOSING states.
  *  The security module may store data in the ext_session parameter. This data MUST be freed
  *  when the new pss state is PSS_CLOSED.
  *
@@ -249,9 +246,8 @@
  * CALLBACK:	sec_is_supported_peer_cb_t
  *
  * PARAMETERS:
- *   diamid     : The supposed diameter-id of the peer. Before CER/CEA exchange, this may also be another fqdn of the peer.
- *                The real diameter-id of the peer is available by using the peer_t pointer from the sec_session_t later.
- *   sa         : Information about the location and port of the diameter peer we are wanting to connect to.
+ *   diamid     : The diameter-id of the peer.
+ *   sa         : Information about the location and port of the diameter peer we connected to.
  *   priority   : Upon success, the priority of this module for handling this peer is written here. 
  *                Negative value means that the module can not handle this peer.
  *
--- a/waaad/Makefile.am	Thu Sep 18 18:28:58 2008 +0900
+++ b/waaad/Makefile.am	Mon Sep 29 16:13:56 2008 +0900
@@ -34,7 +34,7 @@
 		peer-internal.h peer-listener.c \
 		peer-psm.c peer-send.c peer-recv.c \
 		peer-tcp.c peer-sctp.c peer-events.c \
-		peer-expire.c \
+		peer-expire.c peer-sec-ini.c \
 		message.h message.c \
 		queues.h queues.c \
 		routing.h routing.c \
--- a/waaad/log.h	Thu Sep 18 18:28:58 2008 +0900
+++ b/waaad/log.h	Mon Sep 29 16:13:56 2008 +0900
@@ -133,5 +133,9 @@
 	
 #define TRACE_HERE()	\
 	TRACE_DEBUG(FULL, " -- debug checkpoint -- ");
+	
+#define TRACE_DEBUG_ALL( str ) 	\
+	TRACE_DEBUG(FULL+3, str );
+
 
 #endif /* _LOG_H_ */
--- a/waaad/peer-internal.h	Thu Sep 18 18:28:58 2008 +0900
+++ b/waaad/peer-internal.h	Mon Sep 29 16:13:56 2008 +0900
@@ -74,6 +74,17 @@
 #define  INCNX_TIMEOUT	 20	/* in seconds */
 #endif /* INCNX_TIMEOUT */
 
+/* The timeout value to wait for answer to a DPR */
+#ifndef DPR_TIMEOUT
+#define DPR_TIMEOUT 	15	/* in seconds */
+#endif /* DPR_TIMEOUT */
+
+/* Maximum size of a Diameter message that we accept to receive */
+#ifndef DIAMETER_MSG_SIZE_MAX
+#define DIAMETER_MSG_SIZE_MAX	65535	/* in bytes */
+#endif /* DIAMETER_MSG_SIZE_MAX */
+
+
 /* States of a peer */
 typedef enum {
 	/* Stable states */
@@ -98,23 +109,25 @@
 
 /* Events that can be sent to the peer and handled by the peer state machine */
 typedef enum {
-	PEVENT_SHUTDOWN = 1,	/* The daemon requested to shutdown this peer resources */
+	PEVENT_SHUTDOWN = 1,	/* The daemon requested to initiate shutdown of this peer (will go to disabled state) */
+	PEVENT_DESTROY,		/* The daemon requested to destroy this peer (any resources will be freed) */
+	PEVENT_EXPIRE,		/* the peer lifetime is expiring; call p_addinfo.pa_cb or delete */
+			
+	PEVENT_TIMEOUT,		/* the timeout for this state has expired */
 	
-	PEVENT_DISCONNECTED,	/* the socket has been disconnected */
-	PEVENT_EXPIRE,		/* the peer lifetime is expiring; call p_addinfo.pa_cb or delete */
+	PEVENT_CONNECTED,	/* the socket has been connected */
 	PEVENT_MSGRCVD,		/* a message was received */
 	
-	PEVENT_SND_FAILED,	/* Failed to send a message (means probably connection is broken) */
 	PEVENT_ASSOC_CHG,	/* association changed (SCTP notification) */
 	PEVENT_PEERADDR_CHG,	/* peer address changed (SCTP notification) */
 	PEVENT_REMOTE_ERR,	/* Remote error (SCTP notification) */
 	
+	PEVENT_DISCONNECTED,	/* the socket has been disconnected */
+	PEVENT_SND_FAILED,	/* Failed to send a message (means probably connection is broken) */
 	PEVENT_TH_TERM_IN,	/* The incoming thread terminated (on error) */
 	PEVENT_TH_TERM_OUT,	/* The outgoing thread terminated (on error) */
 	
-	PEVENT_TIMEOUT,		/* a timeout has expired */
-	
-	PEVENT_MAX	/* To be continued */
+	PEVENT_MAX	/* Invalid value */
 } _pevent_t;
 
 /* An event element */
@@ -124,8 +137,10 @@
 } _pe_t;
 
 /* Flags definitions */
-#define PEERFL_DW_PENDING	( 1 << 0 )	/* A DWR message was sent and not answered yet */
-#define PEERFL_CNX_PB		( 1 << 1 )	/* The peer was disconnected because of watchdogs; must exchange 3 watchdogs before putting back to normal */
+#define PEERFL_DW_PENDING		( 1 << 0 )	/* A DWR message was sent and not answered yet */
+#define PEERFL_CNX_PB			( 1 << 1 )	/* The peer was disconnected because of watchdogs; must exchange 3 watchdogs before putting back to normal */
+#define PEERFL_DISABLE_AFTER_SHUTDOWN	( 1 << 2 )	/* When the peer enters the "CLOSED" state, its PSM must be stopped */
+#define PEERFL_DESTROY_AFTER_SHUTDOWN	( 1 << 3 )	/* When the peer enters the "CLOSED" state, its PSM must be stopped and resources freed */
 
 /* Peer internal description */
 typedef struct {
@@ -168,6 +183,7 @@
 	uint16_t	 p_istr;	/* number of inbound streams (for SCTP) */
 	uint16_t	 p_curstr;	/* id of the last used out stream */
 	int		 p_sock_tmp;	/* In case of election, store the socket on which the CER was received here, temporarily */
+	pthread_t	 p_con_th;	/* The thread handling initial connection */
 	size_t		 p_peeraddr_sz;	/* Number of items in the array p_peeraddr bellow */
 	sSS 		*p_peeraddr;	/* Array of the attachment points of the remote peer (received in Host-IP-Address AVPs) */
 	
@@ -207,7 +223,7 @@
 extern pthread_cond_t	_peers_lists_cond;/* the condvar that is used by _peer_expire_th */
 
 /* Reference to the "no sec" security module used before CER is exchanged */
-extern _sec_item_t 	_peer_smi;
+extern sec_module_t _peers_sec_ini;
 
 /* The eye-catcher value */
 #define PEER_EYEC	0x023350B7
--- a/waaad/peer-listener.c	Thu Sep 18 18:28:58 2008 +0900
+++ b/waaad/peer-listener.c	Mon Sep 29 16:13:56 2008 +0900
@@ -129,7 +129,7 @@
 	uti_list_init(&(dummy.p_events), NULL);
 	CHECK_FCT_DO(  meq_new(&(dummy.p_in_q)), goto end  );
 	
-	dummy.p_secmod = _peer_smi.sm;
+	dummy.p_secmod = &_peers_sec_ini;
 	
 	/* the sec_session_t object used by the security module */
 	SEC_SESS_INIT( &(dummy.p_sec_session), ci->proto, &dummy, &ci->sock );
@@ -216,6 +216,7 @@
 	  /* int _peer_search(char * diamid, size_t diamidlen, uint32_t * hash, uti_list_t ** peerglob) */
 	
 	/* depending on the peer state, act as needed... */
+		/* Check the supported security modules */
 	
 	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
 	
--- a/waaad/peer-psm.c	Thu Sep 18 18:28:58 2008 +0900
+++ b/waaad/peer-psm.c	Mon Sep 29 16:13:56 2008 +0900
@@ -48,11 +48,6 @@
 #include "peer-internal.h"
 
 
-/* The timeout value to wait for answer to a DPR */
-#define DPR_TIMEOUT 15
-
-
-
 /******************************************************************************/
 /*  macros for more dev-friendly traces                                       */
 /******************************************************************************/
@@ -62,12 +57,14 @@
 	"<INVALID>",
 	"DISABLED", 
 	"OPEN",
+	
 	"CLOSED",
 	"CLOSING",
 	"WAIT_CNX_ACK",
 	"WAIT_CNX_ACK_ELEC",
 	"WAIT_CEA",
 	"WAIT_RETURNS_ELEC",
+	
 	"SUSPECT",
 	"REOPEN"
 };
@@ -77,17 +74,24 @@
 /* Convert an event to a string */
 static char * eventstr[] = {
 	"<INVALID>",
+	
 	"SHUTDOWN", 
-	"DISCONNECTED",
+	"DESTROY", 
 	"EXPIRE",
+	
+	"TIMEOUT",
+	
+	"CONNECTED",
 	"MSGRCVD",
-	"SND_FAILED",
+	
 	"ASSOC_CHG",
 	"PEERADDR_CHG",
 	"REMOTE_ERR",
+	
+	"DISCONNECTED",
+	"SND_FAILED",
 	"TH_TERM_IN",
-	"TH_TERM_OUT",
-	"TIMEOUT"
+	"TH_TERM_OUT"
 };
 #define VALEVENT( _event_ ) ( (((_event_) > 0) && ((_event_) < PEVENT_MAX)) ? (_event_) : 0 )
 #define EVENTSTR( _ev_ ) eventstr[VALEVENT( _ev_ )]
@@ -98,6 +102,23 @@
 /*  Functions to reset some parts of a peer object                            */
 /******************************************************************************/
 
+/* Stop any children thread */
+static void _peer_reset_threads( _peer_t * peer )
+{
+	if (peer->p_con_th != (pthread_t) NULL) {
+		CHECK_FCT_DO( _peer_cancel_th(&peer->p_con_th), /* continue */ );
+		peer->p_con_th = (pthread_t) NULL;
+	}
+	if (peer->p_in_th != (pthread_t) NULL) {
+		CHECK_FCT_DO( _peer_cancel_th(&peer->p_in_th), /* continue */ );
+		peer->p_in_th = (pthread_t) NULL;
+	}
+	if (peer->p_out_th != (pthread_t) NULL) {
+		CHECK_FCT_DO( _peer_cancel_th(&peer->p_out_th), /* continue */ );
+		peer->p_out_th = (pthread_t) NULL;
+	}
+}
+
 /* reset the socket(s) */
 static void _peer_reset_sock( _peer_t * peer )
 {
@@ -183,7 +204,7 @@
 		peer->p_sec_hdl = NULL;
 	}
 	
-	peer->p_secmod = _peer_smi.sm;
+	peer->p_secmod = &_peers_sec_ini;
 	
 	if (peer->p_sec_list) {
 		CHECK_FCT_DO(  sec_freemodules( peer->p_sec_list ),  /* nothing */  );
@@ -214,12 +235,16 @@
 	}
 	
 	peer->p_ts.tv_sec += delay;
+	
+#if 1
+	/* temporary for debug */
+	peer->p_ts.tv_sec += 10;
+#endif
 }
 
 
 
 
-
 /******************************************************************************/
 /*  Functions related to the state machine                                    */
 /******************************************************************************/
@@ -254,6 +279,28 @@
 	return 0;
 }
 
+/* Change the security state of a peer */
+static void _peer_change_sec_state(_peer_t * peer, _peer_state_t new)
+{
+	TRACE_ENTRY();
+	
+//	CHECK_FCT_DO( (*peer->p_secmod->sec_state_change)(
+//			/* new state */ PSS_CLOSED,
+//			/* old state */ PSS_CONNECTED,
+//			/* session */ &peer->p_sec_session,
+//			/* private data */ &peer->p_ext_session), 
+//		/* Do nothing else than logging if it fails */ );
+				
+	TRACE_DEBUG(INFO, "Security states: not implemented...");
+	
+	/* First, check if the peer is using the peer-sec-ini module or a real sec module */
+	
+	/* For clean initialization, PSS_CLOSED->PSS_INITIAL->PSS_OPEN */
+	/* For clean termination, PSS_OPEN->PSS_CLOSING->PSS_CLOSED */
+	/* For violent termination, *->PSS_CLOSED */
+	
+	return;	
+}
 
 /* Change the state of a peer */
 static void _peer_change_state(_peer_t * peer, _peer_state_t new)
@@ -262,6 +309,13 @@
 	
 	TRACE_DEBUG(FULL, "Peer '%s' state change: %s -> %s", peer->p_diamid, STATESTR(peer->p_state), STATESTR(new));
 	
+	/* Only proceed if the state actually changed */
+	if (peer->p_state == new)
+		return;
+	
+	/* Now change the state in security module */
+	_peer_change_sec_state(peer, new);
+	
 	/* change the state */
 	peer->p_state = new;
 	
@@ -303,62 +357,148 @@
 	return;
 }
 
+/******************************************************************************/
+/*  Connect to a remote peer (thread)                                         */
+/******************************************************************************/
+static void * _peer_connect_th(void * arg) 
+{
+	_peer_t * peer = _P(arg);
+	int ret = 0;
+	int sock = 0;
+	uint16_t in = 0, out = 0;
+	
+	TRACE_ENTRY("%p", arg);
+	
+	/* Check the parameter */
+	CHECK_PARAMS_DO( VALIDATE_PEER(peer), goto error );
+	
+	/* Now attempt the connection... we'll succeed or be canceled */
+	switch (peer->p_addinfo.pa_proto) {
+		case IPPROTO_SCTP:
+			out = peer->p_addinfo.pa_streams;
+			ret = _peer_sctp_connect( (sSA *) &(peer->p_addinfo.pa_ss), sSSlen(&(peer->p_addinfo.pa_ss)), &sock, &out, &in );
+			break;
+			
+		case IPPROTO_TCP:
+			ret = _peer_tcp_connect( (sSA *) &(peer->p_addinfo.pa_ss), sSSlen(&(peer->p_addinfo.pa_ss)), &sock );
+			break;
+			
+		default:
+			CHECK_PARAMS_DO( 0, goto end );
+	}
+	
+	if (ret == 0) {
+		CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_lock), goto end );
+		if ( peer->p_sock == 0 ) {
+			peer->p_sock = sock;
+			peer->p_ostr = out;
+			peer->p_istr = in;
+		} else {
+			ret = EALREADY;
+			CHECK_SYS_DO( shutdown(sock, SHUT_RDWR), /* continue */ );
+		}
+		CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_lock), goto end );
+	}
+	
+	if (ret == 0) {
+		CHECK_FCT_DO( _peer_sendevent(peer, PEVENT_CONNECTED, NULL), goto end );
+	}
+		
+end:
+	TRACE_DEBUG(FULL, "Connect thread for peer '%s' terminated", peer->p_diamid);
+error:
+	return NULL;
+}
+
+/******************************************************************************/
+/*  Failover of a peer: will resend messages with another path                */
+/******************************************************************************/
+static void _peer_failover( _peer_t * peer )
+{
+	TRACE_ENTRY("%p(%s)", peer, peer->p_diamid?:"???");
+	
+	TRACE_DEBUG(INFO, "FAILOVER: not implemented yet...");
+	return;
+}
+
+
+/******************************************************************************/
+/*  State machine callbacks to handle events                                  */
+/******************************************************************************/
+
+/* For events that we don't handle yet */
+static int _psm_notsup( _peer_t * peer, _pevent_t event, void * ev_data )
+{
+	TRACE_DEBUG(FULL, "'%s' in state '%s', HANDLER FOR '%s' NOT IMPLEMENTED", peer->p_diamid, STATESTR(peer->p_state), EVENTSTR(event));
+	_peer_reset_ts(peer, 0, 5); /* For debug... */
+	return 0;
+}
 
 /* callback that should never be called */
-static int _psm_assert( _peer_t * peer, _peer_state_t prev, void * ev_data )
+static int _psm_assert( _peer_t * peer, _pevent_t event, void * ev_data )
 {
 	TRACE_DEBUG(INFO, "Bad situation on peer state machine '%s'!!!", peer->p_diamid);
 	ASSERT(0);
 	return 2;
 }
 
-/* For events that we don't handle */
-static int _psm_ignore( _peer_t * peer, _peer_state_t prev, void * ev_data )
+/* For events that are ignored */
+static int _psm_ignore( _peer_t * peer, _pevent_t event, void * ev_data )
 {
-	TRACE_DEBUG(FULL, "'%s' in state '%s', ignoring an event (/HANDLER NOT IMPLEMENTED)", peer->p_diamid, STATESTR(prev));
+	TRACE_DEBUG(FULL, "'%s' in state '%s', ignoring event '%s'...", peer->p_diamid, STATESTR(peer->p_state), EVENTSTR(event));
 	return 0;
 }
 
-/* When the peer is requested to shutdown, send a DPR message. The reason may be passed as event data ? */
-static int _psm_send_DPR( _peer_t * peer, _peer_state_t prev, void * ev_data )
-{
-	TRACE_ENTRY("%p(%s) %d(%s) %p", peer, peer->p_diamid ?: "null", prev, STATESTR(prev), ev_data);
-	
-	TRACE_DEBUG(FULL, "Requesting clean disconnect to '%s'", peer->p_diamid);
-	
-	/* @@ TODO: send a DPR message */
-	TRACE_DEBUG(INFO, "DPR: not implemented yet...");
-	
-	/* re-arm the timer */
-	_peer_reset_ts(peer, 0, DPR_TIMEOUT);
-	
-	return 0;
-}
 
 /* The connection is broken, we must end it abruptly */
-static int _psm_disconnect( _peer_t * peer, _peer_state_t prev, void * ev_data )
+static int _psm_disconnect( _peer_t * peer, _pevent_t event, void * ev_data )
 {
-	TRACE_ENTRY("%p(%s) %d(%s) %p", peer, peer->p_diamid ?: "null", prev, STATESTR(prev), ev_data);
+	TRACE_ENTRY("%p(%s/%s) %d(%s) %p", peer, peer->p_diamid ?: "null", STATESTR(peer->p_state), event, EVENTSTR(event), ev_data);
 	
-	/* @@ TODO */
-	TRACE_DEBUG(INFO, "Not implemented yet...");
+	/* Actually all the processing is done by reseting the peer, so just return reset request here */
 	return 1;
 }
 
 /* The lifetime of the peer entry is terminated, we must expire it or refresh it */
-static int _psm_expire( _peer_t * peer, _peer_state_t prev, void * ev_data )
+static int _psm_expire( _peer_t * peer, _pevent_t event, void * ev_data )
 {
-	TRACE_ENTRY("%p(%s) %d(%s) %p", peer, peer->p_diamid ?: "null", prev, STATESTR(prev), ev_data);
+	TRACE_ENTRY("%p(%s/%s) %d(%s) %p", peer, peer->p_diamid ?: "null", STATESTR(peer->p_state), event, EVENTSTR(event), ev_data);
 	
 	/* @@ TODO */
 	TRACE_DEBUG(INFO, "Not implemented yet...");
 	return 2;
 }
 
-/* A message has been received, we must handle application 0 and forward others to the routing module */
-static int _psm_receive( _peer_t * peer, _peer_state_t prev, void * ev_data )
+
+/* Attempt a new connection */
+static int _psm_connect( _peer_t * peer, _pevent_t event, void * ev_data )
 {
-	TRACE_ENTRY("%p(%s) %d(%s) %p", peer, peer->p_diamid ?: "null", prev, STATESTR(prev), ev_data);
+	TRACE_ENTRY("%p(%s/%s) %d(%s) %p", peer, peer->p_diamid ?: "null", STATESTR(peer->p_state), event, EVENTSTR(event), ev_data);
+	
+	CHECK_PARAMS_DO( peer->p_diamid, return 2 );
+	
+	TRACE_DEBUG(INFO, "Connecting to peer '%s'...", peer->p_diamid);
+	
+	/* go to STATE_WAITCNXACK state */
+	_peer_change_state(peer, STATE_WAITCNXACK);
+	
+	/* Start a thread to attempt new connection. That thread will send an event upon success, or be canceled on timeout. */
+	TRACE_DEBUG(INFO, "Not implemented yet...");
+	
+	/* Create the thread to connect to the peer */
+	CHECK_POSIX_DO( pthread_create( &peer->p_con_th, NULL, _peer_connect_th, peer ),  return 1 );
+	
+	/* and re-arm the timer */
+	_peer_reset_ts(peer, 0, CNX_TIMEOUT);
+
+	return 0;
+}
+
+
+/* A message has been received, we must handle application 0 and forward others to the routing module (if we are in open state) */
+static int _psm_receive( _peer_t * peer, _pevent_t event, void * ev_data )
+{
+	TRACE_ENTRY("%p(%s/%s) %d(%s) %p", peer, peer->p_diamid ?: "null", STATESTR(peer->p_state), event, EVENTSTR(event), ev_data);
 	
 	/* @@ TODO */
 	TRACE_DEBUG(INFO, "Not implemented yet...");
@@ -368,150 +508,210 @@
 	return 0;
 }
 
+
 /* long time since data was received */
-static int _psm_do_watchdog( _peer_t * peer, _peer_state_t prev, void * ev_data )
+static int _psm_do_watchdog( _peer_t * peer, _pevent_t event, void * ev_data )
 {
-	TRACE_ENTRY("%p(%s) %d(%s) %p", peer, peer->p_diamid ?: "null", prev, STATESTR(prev), ev_data);
+	TRACE_ENTRY("%p(%s/%s) %d(%s) %p", peer, peer->p_diamid ?: "null", STATESTR(peer->p_state), event, EVENTSTR(event), ev_data);
 	
-	/* @@ TODO */
-	TRACE_DEBUG(INFO, "Not implemented yet...");
+	/* Have we already sent a DWR? */
+	if (peer->p_flags & PEERFL_DW_PENDING) {
+		/* Ok, the remote peer did not answer in the given time, let's failover */
+		
+		/* Mark the peer as suspect */
+		_peer_change_state(peer, STATE_SUSPECT);
+		
+		/* requeue all messages in global structures */
+		_peer_failover(peer);
+		
+	} else {
+		/* TwTimer has passed since last received message, so send a DWR to probe the cnx */
+		
+		TRACE_DEBUG(INFO, "DWR: Not implemented yet...");
+		
+		/* Then arm the flag */
+		peer->p_flags |= PEERFL_DW_PENDING;
+	}
+	
+	/* and re-arm the timer */
+	_peer_reset_ts(peer, 1, g_pconf->twtimer);
+
 	return 0;
 }
 
+
+/* When the peer is requested to shutdown, send a DPR message. The reason may be passed as event data */
+static int _psm_send_DPR( _peer_t * peer, _pevent_t event, void * ev_data )
+{
+	TRACE_ENTRY("%p(%s/%s) %d(%s) %p", peer, peer->p_diamid ?: "null", STATESTR(peer->p_state), event, EVENTSTR(event), ev_data);
+	
+	TRACE_DEBUG(FULL, "Requesting clean disconnection to '%s'", peer->p_diamid);
+	
+	/* Set the flag PEERFL_DISABLE_AFTER_SHUTDOWN or PEERFL_DESTROY_AFTER_SHUTDOWN according to the event */
+	
+	/* @@ TODO: send a DPR message. The reason may be indicated in ev_data. */
+	TRACE_DEBUG(INFO, "DPR: not implemented yet...");
+	
+	/* re-arm the timer */
+	_peer_reset_ts(peer, 0, DPR_TIMEOUT);
+	
+	return 0;
+}
+
+
 /* The following matrix is the state machine. See following function for more details */
 static struct {
-	int 		(*cb)(_peer_t * peer, _peer_state_t prev, void * ev_data);
-	_peer_state_t 	  nextstate;
+	int 		(*cb)(_peer_t * peer, _pevent_t event, void * ev_data);
 } PSM_ARRAY [ /* previous state */ ] [ PEVENT_MAX ] = {
 	/* STATE_DISABLED 	*/  {	  /* In this state, there is no PSM running, so any call here is erroneous */
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_assert,	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_assert, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_assert, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_assert		},
+		/* PEVENT_DESTROY 	*/ {  _psm_assert		},
+		/* PEVENT_EXPIRE 	*/ {  _psm_assert		},
+		/* PEVENT_TIMEOUT 	*/ {  _psm_assert		},
+		/* PEVENT_CONNECTED 	*/ {  _psm_assert		},
+		/* PEVENT_MSGRCVD 	*/ {  _psm_assert		},
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_assert		},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_assert		},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_assert		},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_assert		},
+		/* PEVENT_SND_FAILED 	*/ {  _psm_assert		},
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_assert		},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_assert		}
 		},
 	/* STATE_OPEN 	*/  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_send_DPR, 	STATE_CLOSING	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_disconnect, 	STATE_CLOSED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_expire, 	STATE_OPEN	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_receive, 	STATE_OPEN	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_disconnect, 	STATE_CLOSED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_OPEN	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_OPEN	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_OPEN	}, /* should mark as SUSPECT and probe the cnx with DWR/DWA ? */
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_disconnect, 	STATE_CLOSED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_disconnect, 	STATE_CLOSED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_do_watchdog,	STATE_OPEN	}  /* the handler may overwrite the next state to SUSPECT */
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_send_DPR		}, /* set flag PEERFL_DISABLE_AFTER_SHUTDOWN then initiate disconnection procedure */
+		/* PEVENT_DESTROY 	*/ {  _psm_send_DPR		}, /* set flag PEERFL_DESTROY_AFTER_SHUTDOWN then initiate disconnection procedure */
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire		}, /* Handle dynamic peers end-of-lifetime */
+		/* PEVENT_TIMEOUT 	*/ {  _psm_do_watchdog		}, /* the handler may overwrite the next state to SUSPECT */
+		/* PEVENT_CONNECTED 	*/ {  _psm_assert		}, /* Did we initiate two connections?? */
+		/* PEVENT_MSGRCVD 	*/ {  _psm_receive		}, /* Handle received messages */
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_notsup		}, /* Security issue? We may want to check the address change is compatible with advertised host-ip-addresses */
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_notsup		}, /* should mark as SUSPECT and probe the cnx with DWR/DWA ? */
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_disconnect		}, /* Cleanup open resources and reset the PSM */
+		/* PEVENT_SND_FAILED 	*/ {  _psm_disconnect		}, /* Cleanup open resources and reset the PSM */
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_disconnect		}, /* A serious error occurred in receiver thread: cleanup open resources and reset the PSM */
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_disconnect		}  /* A serious error occurred in sender   thread: cleanup open resources and reset the PSM */
 		},
 	/* STATE_CLOSED 	*/  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_ignore, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore 	 	}, /* The peer is already shutdown. */
+		/* PEVENT_DESTROY 	*/ {  _psm_notsup 	 	},
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire 	 	}, /* Handle dynamic peers end-of-lifetime */
+		/* PEVENT_TIMEOUT 	*/ {  _psm_connect	 	}, /* Timeout reached, it's time to attempt new connection */
+		/* PEVENT_CONNECTED 	*/ {  _psm_assert 	 	}, /* This should never happen, but anyway we can ignore it */
+		/* PEVENT_MSGRCVD 	*/ {  _psm_assert 	 	}, /* Assert for the moment, anyway we should be able to handle the message */
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore 	 	}, 
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore 	 	},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore 	 	},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore 	 	}, /* This should never happen, but anyway we can ignore it */
+		/* PEVENT_SND_FAILED 	*/ {  _psm_assert 	 	}, /* Of course send would fail on a closed peer... */
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_assert 	 	},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_assert 	 	}
 		},
 	/* STATE_CLOSING 	*/  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_ignore, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_notsup	 	},
+		/* PEVENT_DESTROY 	*/ {  _psm_notsup	 	},
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire	 	},
+		/* PEVENT_TIMEOUT 	*/ {  _psm_notsup	 	},
+		/* PEVENT_CONNECTED 	*/ {  _psm_notsup	 	},
+		/* PEVENT_MSGRCVD 	*/ {  _psm_notsup	 	},
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_notsup	 	},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_notsup	 	},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_notsup	 	},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_notsup	 	},
+		/* PEVENT_SND_FAILED 	*/ {  _psm_notsup	 	},
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_notsup	 	},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_notsup	 	}
 		},
 	/* STATE_WAITCNXACK 	*/  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_ignore, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_notsup	 	},
+		/* PEVENT_DESTROY 	*/ {  _psm_notsup		},
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire		},
+		/* PEVENT_TIMEOUT 	*/ {  _psm_notsup		},
+		/* PEVENT_CONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_MSGRCVD 	*/ {  _psm_notsup		},
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_notsup		},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_SND_FAILED 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_notsup		}
 		},
 	/* STATE_WAITCNXACK_ELEC */  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_ignore, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_notsup		},
+		/* PEVENT_DESTROY 	*/ {  _psm_notsup		},
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire		},
+		/* PEVENT_TIMEOUT 	*/ {  _psm_notsup		},
+		/* PEVENT_CONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_MSGRCVD 	*/ {  _psm_notsup		},
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_notsup		},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_SND_FAILED 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_notsup		}
 		},
 	/* STATE_WAITCEA 	*/  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_ignore, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_notsup		},
+		/* PEVENT_DESTROY 	*/ {  _psm_notsup		},
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire		},
+		/* PEVENT_TIMEOUT 	*/ {  _psm_notsup		},
+		/* PEVENT_CONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_MSGRCVD 	*/ {  _psm_receive		},
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_notsup		},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_SND_FAILED 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_notsup		}
 		},
 	/* STATE_WAITRETURNS_ELEC*/  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_ignore, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_notsup		},
+		/* PEVENT_DESTROY 	*/ {  _psm_notsup		},
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire		},
+		/* PEVENT_TIMEOUT 	*/ {  _psm_notsup		},
+		/* PEVENT_CONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_MSGRCVD 	*/ {  _psm_notsup		},
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_notsup		},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_SND_FAILED 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_notsup		}
 		},
 	/* STATE_SUSPECT 	*/  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_ignore, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_notsup		},
+		/* PEVENT_DESTROY 	*/ {  _psm_notsup		},
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire		},
+		/* PEVENT_TIMEOUT 	*/ {  _psm_notsup		},
+		/* PEVENT_CONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_MSGRCVD 	*/ {  _psm_notsup		},
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_notsup		},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_SND_FAILED 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_notsup		}
 		},
 	/* STATE_REOPEN 	*/  {
-		/* PEVENT_SHUTDOWN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_DISCONNECTED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_EXPIRE 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_MSGRCVD 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_SND_FAILED 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_ASSOC_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_REMOTE_ERR 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_IN 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_ignore, 	STATE_DISABLED	},
-		/* PEVENT_TIMEOUT 	*/ {  _psm_ignore, 	STATE_DISABLED	}
+		/* PEVENT_SHUTDOWN 	*/ {  _psm_notsup		},
+		/* PEVENT_DESTROY 	*/ {  _psm_notsup		},
+		/* PEVENT_EXPIRE 	*/ {  _psm_expire		},
+		/* PEVENT_TIMEOUT 	*/ {  _psm_notsup		},
+		/* PEVENT_CONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_MSGRCVD 	*/ {  _psm_notsup		},
+		/* PEVENT_ASSOC_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_PEERADDR_CHG 	*/ {  _psm_notsup		},
+		/* PEVENT_REMOTE_ERR 	*/ {  _psm_notsup		},
+		/* PEVENT_DISCONNECTED 	*/ {  _psm_notsup		},
+		/* PEVENT_SND_FAILED 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_IN 	*/ {  _psm_notsup		},
+		/* PEVENT_TH_TERM_OUT 	*/ {  _psm_notsup		}
 		}
 };	
 
@@ -527,22 +727,39 @@
 static int do_state_machine(_peer_t * peer) 
 {
 	_pevent_t event;
-	_peer_state_t state;
 	void * event_data;
 	
-	TRACE_DEBUG(FULL, "Enter PSM peer '%s' state '%s'...", peer->p_diamid, STATESTR(peer->p_state));
+	TRACE_DEBUG(FULL, "PSM for peer '%s' state '%s', waiting next event...", peer->p_diamid, STATESTR(peer->p_state));
 	
 	/* wait for the next event (or timeout). On error destroy the peer completly */
 	CHECK_FCT_DO( wait_next_event(peer, &event, &event_data), return 2 );
 	
-	/* update the peer state according to the PSM */
-	state = peer->p_state;
-	_peer_change_state(peer, PSM_ARRAY[state - 1][event - 1].nextstate);
-	
+	TRACE_DEBUG(FULL, "PSM for peer '%s' state '%s', received: '%s', processing...", peer->p_diamid, STATESTR(peer->p_state), EVENTSTR(event));
+
 	/* now call the PSM callback */
-	return (*PSM_ARRAY[state - 1][event - 1].cb)(peer, state, event_data);
+	return (*PSM_ARRAY[peer->p_state - 1][event - 1].cb)(peer, event, event_data);
 }
 
+/* In case of cancellation, we must unlock the peer */
+static void _peer_psm_cleanup( void * arg )
+{
+	_peer_t * peer = (_peer_t *) arg;
+	
+	TRACE_ENTRY("%p", peer);
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock( &peer->p_lock ), /* ignore */ );
+	
+	if (peer->p_in_th != (pthread_t) NULL) {
+		CHECK_FCT_DO( _peer_cancel_th(&peer->p_in_th), /* continue */ );
+		peer->p_in_th = (pthread_t) NULL;
+	}
+	if (peer->p_out_th != (pthread_t) NULL) {
+		CHECK_FCT_DO( _peer_cancel_th(&peer->p_out_th), /* continue */ );
+		peer->p_out_th = (pthread_t) NULL;
+	}
+	
+	return;
+}
 
 /* The code of the p_psm_th thread */
 void * _peer_state_machine_th(void * arg)
@@ -566,6 +783,8 @@
 		
 		
 		TRACE_DEBUG(INFO, "Not implemented");
+		
+		
 		goto destroy_me;
 	} else {
 		
@@ -577,7 +796,9 @@
 psm:
 	/* the peer state machine main loop */
 	while (1) {
+		pthread_cleanup_push( _peer_psm_cleanup, (void *)peer );
 		ret = do_state_machine(peer);
+		pthread_cleanup_pop(0);
 		switch (ret) {
 			case 0:	/* no problem so far, continue to next event */
 				continue;
@@ -607,14 +828,10 @@
 	_peer_reset_events(peer);
 	
 	/* Stop in and out threads if needed */
-	if (peer->p_in_th != (pthread_t) NULL) {
-		CHECK_FCT_DO( _peer_cancel_th(&peer->p_in_th), /* continue */ );
-		peer->p_in_th = (pthread_t) NULL;
-	}
-	if (peer->p_out_th != (pthread_t) NULL) {
-		CHECK_FCT_DO( _peer_cancel_th(&peer->p_out_th), /* continue */ );
-		peer->p_out_th = (pthread_t) NULL;
-	}
+	_peer_reset_threads(peer);
+	
+	/* Failover: requeue all sent messages and pending messages to the global outgoing queue */
+	_peer_failover(peer);
 	
 	/* discard any pending incoming or outgoing message -- very rare situation probably */
 	_peer_reset_pendingmsg(peer);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/waaad/peer-sec-ini.c	Mon Sep 29 16:13:56 2008 +0900
@@ -0,0 +1,231 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2008, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Peers facility.
+ *
+ * This file contains the code of a dummy security module to exchange data with a remote peer before identity is known.
+ *
+ */
+
+#include "waaad-internal.h"
+#include "peer-internal.h"	
+
+/* State change callback */
+static int _psi_state_change (sec_pss_t newstate, sec_pss_t oldstate, sec_session_t * session, void ** ext_session)
+{
+	TRACE_ENTRY("%d %d %p %p", newstate, oldstate, session, ext_session);
+	
+	/* We don't need to save a session state in this module, so do not do anything here */
+	return 0;
+}
+
+/* Sending data: we just write the buffer unmodified on the connection object */
+static int _psi_send (sec_session_t * session, void ** ext_session, void * data, size_t length)
+{
+	ssize_t ret = 0;
+	size_t sent = 0;
+	int  stream = -1;
+	
+	TRACE_ENTRY("%p %p %p %d", session, ext_session, data, length);
+	
+	if (!session || !session->conn) {
+		TRACE_DEBUG(INFO, "Invalid argument");
+		return EINVAL;
+	}
+	
+	while (sent < length) {
+		switch (session->proto) {
+			case IPPROTO_TCP:
+				ret = (*session->cbs.tcp.send_data) (session, ((char *)data) + sent, length - sent);
+				break;
+				
+			case IPPROTO_SCTP:
+				ret = (*session->cbs.sctp.send_data) (session, &stream, ((char *)data) + sent, length - sent);
+				break;
+			
+			default:
+				TRACE_DEBUG(INFO, "Invalid proto");
+				return EINVAL;
+		}
+				
+		if (ret == -1) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "The send_data callback failed: %s", strerror(ret));
+			return ret;
+		}
+		sent += ret;
+	}
+	
+	return 0;
+}
+
+static int _psi_recv_tcp (sec_session_t * session, void ** data, size_t *length);
+static int _psi_recv_sctp(sec_session_t * session, void ** data, size_t *length);
+
+/* We just receive the buffer on the connection object, and rebuild a message (boundaries are lost with TCP) */
+int _psi_recv (sec_session_t * session, void ** ext_session, void ** data, size_t *length)
+{
+	ssize_t ret = 0;
+	
+	TRACE_ENTRY("%p %p %p %p", session, ext_session, data, length);
+	
+	if (!session || !session->conn || !data || !length) {
+		TRACE_DEBUG(INFO, "Invalid argument");
+		return EINVAL;
+	}
+	
+	switch (session->proto) {
+		case IPPROTO_TCP:
+			ret = _psi_recv_tcp(session, data, length);
+			break;
+
+		case IPPROTO_SCTP:
+			ret = _psi_recv_sctp(session, data, length);
+			break;
+
+		default:
+			TRACE_DEBUG(INFO, "Invalid proto");
+			return EINVAL;
+	}
+	
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "sns_recv_unprotect_<proto> failed: %s", strerror(ret));
+	}
+	
+	return ret;
+}
+
+/* Receive from a TCP connection: rebuild the message boundaries */
+static int _psi_recv_tcp (sec_session_t * session, void ** data, size_t *length)
+{
+	unsigned char header[4];
+	unsigned char * newmsg;
+	ssize_t ret = 0;
+	size_t	received = 0;
+	
+	/* First, receive only a message header. */
+	while (received < sizeof(header)) {
+		ret = (*session->cbs.tcp.recv_data) (session, &header[received], sizeof(header) - received);
+		if (ret == 0) {
+			/* Shutdown in progress */
+			TRACE_DEBUG(INFO, "The recv_data function returned 0");
+			return ENOTCONN;
+		}
+		if (ret < 0) {
+			/* Error */
+			ret = errno;
+			TRACE_DEBUG(INFO, "The recv_data function failed: %s", strerror(ret));
+			return ret;
+		}
+		received += ret;
+	}
+	
+	*length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
+	
+	/* Check the received word is a valid begining of a Diameter message */
+	if ((header[0] != MSG_VERSION)	/* MSG_VERSION defined in <waaad/message-api.h> */
+	   || (*length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
+		/* The message is suspect */
+		TRACE_DEBUG(INFO, "Received suspect message header: ver = %d, size = %d", (int)header[0], *length);
+		return EBADMSG;
+	}
+	
+	/* Ok, now we can really receive the data */
+	newmsg = malloc( *length );
+	if (newmsg == NULL) {
+		log_error("Memory allocation failed: %s\n", strerror(errno));
+		TRACE_DEBUG(INFO, "malloc failed");
+		return ENOMEM;
+	}
+	
+	memcpy(newmsg, header, sizeof(header));
+	while (received < *length) {
+		pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
+		ret = (*session->cbs.tcp.recv_data) (session, newmsg + received, (*length) - received);
+		pthread_cleanup_pop(0);
+	
+		if (ret == 0) {
+			/* Shutdown in progress */
+			TRACE_DEBUG(INFO, "The recv_data function returned 0");
+			free(newmsg);
+			return ENOTCONN;
+		}
+		if (ret < 0) {
+			/* Error */
+			ret = errno;
+			TRACE_DEBUG(INFO, "The recv_data function failed: %s", strerror(ret));
+			free(newmsg);
+			return ret;
+		}
+		received += ret;
+	}
+	
+	/* We have received a full message, return it */
+	*data = (void *) newmsg;
+	
+	return 0;
+}
+
+/* Receive from a SCTP connection: the message boundaries are preserved, so we have very few to do here */
+static int _psi_recv_sctp (sec_session_t * session, void ** data, size_t *length)
+{
+	ssize_t ret = 0;
+	uint16_t stream = 0;
+	
+	ret = (*session->cbs.sctp.recv_data) (session, &stream, data, length);
+	if (ret == 0) {
+		/* Shutdown in progress */
+		TRACE_DEBUG(INFO, "The recv_data function returned 0");
+		return ENOTCONN;
+	}
+	if (ret < 0) {
+		/* Error */
+		ret = errno;
+		TRACE_DEBUG(INFO, "The recv_data function failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	TRACE_DEBUG(FULL, "Received a message of %d bytes on stream %d", *length, stream);
+	return 0;
+}
+
+sec_module_t _peers_sec_ini = {
+	.sec_insecid 		= 0,
+	.sec_is_supported_peer 	= NULL,
+	.sec_state_change 	= _psi_state_change,
+	.sec_send_protect 	= _psi_send,
+	.sec_recv_unprotect 	= _psi_recv
+};
+
--- a/waaad/peer.c	Thu Sep 18 18:28:58 2008 +0900
+++ b/waaad/peer.c	Mon Sep 29 16:13:56 2008 +0900
@@ -61,10 +61,6 @@
 /* Has peer_start() been called? */
 static int _peer_started = 0;
 
-/* Security module */
-_sec_item_t _peer_smi = { .sm = NULL };
-
-
 /* Initialize the module */
 int peer_init ( void )
 {
@@ -237,13 +233,6 @@
 	
 	TRACE_ENTRY( );
 	
-	/* The security module is set to the "no_sec" module. 
-	 This simplifies the process of receiving the message. 
-	 This may need to be changed if the security module is different... */
-	if (_peer_smi.sm == NULL) {
-		CHECK_FCT(  sec_module(0, &_peer_smi)  );
-	}
-	
 	/* Start all peer's state machine threads */
 	for (i = 0; i < sizeof(_peer_hash) / sizeof(_peer_hash[0]); i++) {
 		uti_list_t * li;
@@ -433,7 +422,7 @@
 /* Cancel and join a thread */
 int _peer_cancel_th(pthread_t * thread)
 {
-	TRACE_ENTRY( "%p", thread );
+	TRACE_ENTRY( "%p(%p)", thread, (void *)(*thread) );
 	
 	/* Cancel the thread. */
 	(void) pthread_cancel(*thread);
--- a/waaad/tests/Makefile.am	Thu Sep 18 18:28:58 2008 +0900
+++ b/waaad/tests/Makefile.am	Mon Sep 29 16:13:56 2008 +0900
@@ -24,7 +24,7 @@
 		../peer-internal.h ../peer-listener.c \
 		../peer-psm.c ../peer-send.c ../peer-recv.c \
 		../peer-tcp.c ../peer-sctp.c ../peer-events.c \
-		../peer-expire.c \
+		../peer-expire.c ../peer-sec-ini.c \
 		../message.h ../message.c \
 		../queues.h ../queues.c \
 		../routing.h ../routing.c \
--- a/waaad/utils.h	Thu Sep 18 18:28:58 2008 +0900
+++ b/waaad/utils.h	Mon Sep 29 16:13:56 2008 +0900
@@ -64,7 +64,9 @@
 
 /* Check the return value of a system function and execute fallback in case of error */
 #define CHECK_SYS_DO( __call__, __fallback__  ) { 					\
-	int __ret__ = (__call__);							\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( #__call__ );							\
+	__ret__ = (__call__);								\
 	if (__ret__ < 0) {								\
 		int __err__ = errno;	/* We may handle EINTR here */			\
 		log_error("An unexpected error occured (%s), turn on debug for detail\n",\
@@ -75,7 +77,9 @@
 }
 /* Check the return value of a system function, return error code on error */
 #define CHECK_SYS( __call__  ) { 							\
-	int __ret__ = (__call__);							\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( #__call__ );							\
+	__ret__ = (__call__);								\
 	if (__ret__ < 0) {								\
 		int __err__ = errno;	/* We may handle EINTR here */			\
 		log_error("An unexpected error occured (%s), turn on debug for detail\n",\
@@ -87,7 +91,9 @@
 
 /* Check the return value of a POSIX function and execute fallback in case of error or special value */
 #define CHECK_POSIX_DO2( __call__, __val__, __fallback1__, __fallback2__ ) {			\
-	int __ret__ = (__call__);								\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( #__call__ );							\
+	__ret__ = (__call__);								\
 	if (__ret__ != 0) {									\
 		if (__ret__ == (__val__)) {							\
 			__fallback1__;								\
@@ -111,7 +117,9 @@
 
 /* Check that a memory allocator did not return NULL, otherwise log an error and execute fallback */
 #define CHECK_MALLOC_DO( __call__, __fallback__ ) { 					\
-	void * __ret__ = (void *)( __call__ );						\
+	void *  __ret__;								\
+	TRACE_DEBUG_ALL( #__call__ );							\
+	__ret__ = (void *)( __call__ );							\
 	if (__ret__ == NULL) {								\
 		int __err__ = errno;							\
 		log_error("Memory allocation failed: %s\n", strerror(__err__));		\
@@ -129,6 +137,7 @@
 
 /* Check parameters at function entry, execute fallback on error */
 #define CHECK_PARAMS_DO( __bool__, __fallback__ )					\
+	TRACE_DEBUG_ALL( "Check: " #__bool__ );						\
 	if ( ! (__bool__) ) {								\
 		TRACE_DEBUG(INFO, "Invalid parameter received in " #__bool__ );		\
 		__fallback__;								\
@@ -139,7 +148,9 @@
 
 /* Check the return value of an internal function, log and propagate */
 #define CHECK_FCT_DO( __call__, __fallback__ ) {					\
-	int __ret__ = (__call__);							\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( #__call__ );							\
+	__ret__ = (__call__);								\
 	if (__ret__ != 0) {								\
 		TRACE_DEBUG(INFO, "Error in '" #__call__ "': %s", strerror(__ret__));	\
 		__fallback__;								\
@@ -252,6 +263,12 @@
 	}							\
 }
 
+/* The sockaddr length of a sSS structure */
+#define sSSlen( _ss_ )	\
+	( (socklen_t) ( ((_ss_)->ss_family == AF_INET) ? (sizeof(sSA4)) :		\
+				(((_ss_)->ss_family == AF_INET6) ? (sizeof(sSA6)) :	\
+					0 ) ) )
+
 /* Define the value of IP loopback address */
 #ifndef INADDR_LOOPBACK 
 #define INADDR_LOOPBACK	inet_addr("127.0.0.1")
"Welcome to our mercurial repository"