changeset 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
files freeDiameter/CMakeLists.txt freeDiameter/fD.h freeDiameter/fdd.y freeDiameter/p_ce.c freeDiameter/p_cnx.c freeDiameter/p_expiry.c freeDiameter/p_out.c freeDiameter/p_psm.c freeDiameter/peers.c include/freeDiameter/freeDiameter.h libfreeDiameter/messages.c
diffstat 11 files changed, 424 insertions(+), 259 deletions(-) [+]
line wrap: on
line diff
--- a/freeDiameter/CMakeLists.txt	Mon Nov 02 17:31:36 2009 +0900
+++ b/freeDiameter/CMakeLists.txt	Thu Nov 05 14:28:46 2009 +0900
@@ -20,6 +20,7 @@
 	queues.c
 	peers.c
 	p_ce.c
+	p_cnx.c
 	p_dw.c
 	p_dp.c
 	p_expiry.c
--- a/freeDiameter/fD.h	Mon Nov 02 17:31:36 2009 +0900
+++ b/freeDiameter/fD.h	Thu Nov 05 14:28:46 2009 +0900
@@ -118,15 +118,13 @@
 	
 	/* Some flags influencing the peer state machine */
 	struct {
-		unsigned pf_responder	: 1;	/* The local peer is responder on the connection */
+		unsigned pf_responder	: 1;	/* The peer has been created to handle incoming connection */
 		
 		unsigned pf_dw_pending 	: 1;	/* A DWR message was sent and not answered yet */
 		
 		unsigned pf_cnx_pb	: 1;	/* The peer was disconnected because of watchdogs; must exchange 3 watchdogs before putting back to normal */
 		unsigned pf_reopen_cnt	: 2;	/* remaining DW to be exchanged after re-established connection */
 		
-		/* to be completed */
-		
 	}		 p_flags;
 	
 	/* The events queue, peer state machine thread, timer for states timeouts */
@@ -144,6 +142,14 @@
 	/* Sent requests (for fallback), list of struct sentreq ordered by hbh */
 	struct sr_list	 p_sr;
 	
+	/* Data for transitional states before the peer is in OPEN state */
+	struct {
+		struct cnxctx * p_initiator;	/* Connection before CEA is received */
+		struct cnxctx * p_receiver;	/* Only used in case of election */
+		pthread_t	p_ini_thr;
+	};
+		
+	
 	/* connection context: socket and related information */
 	struct cnxctx	*p_cnxctx;
 	
@@ -175,9 +181,12 @@
 	/* Endpoints of a connection have been changed (multihomed SCTP). */
 	,FDEVP_CNX_EP_CHANGE
 	
-	/* A new connection has been established (data contains the appropriate info) */
+	/* A new connection (with a CER) has been received */
 	,FDEVP_CNX_INCOMING
 	
+	/* A new connection has been established to the remote peer (event data is the cnxctx object) */
+	,FDEVP_CNX_ESTABLISHED
+	
 	/* The PSM state is expired */
 	,FDEVP_PSM_TIMEOUT
 	
@@ -195,6 +204,7 @@
 		case_str(FDEVP_CNX_ERROR);		\
 		case_str(FDEVP_CNX_EP_CHANGE);		\
 		case_str(FDEVP_CNX_INCOMING);		\
+		case_str(FDEVP_CNX_ESTABLISHED);	\
 		case_str(FDEVP_PSM_TIMEOUT);		\
 	}						\
 	TRACE_DEBUG(FULL, "Unknown event : %d", event);	\
--- a/freeDiameter/fdd.y	Mon Nov 02 17:31:36 2009 +0900
+++ b/freeDiameter/fdd.y	Thu Nov 05 14:28:46 2009 +0900
@@ -315,9 +315,8 @@
 			
 connpeer:		{
 				memset(&fddpi, 0, sizeof(fddpi));
+				fddpi.config.pic_flags.persist = PI_PRST_ALWAYS;
 				fd_list_init( &fddpi.pi_endpoints, NULL );
-				fd_list_init( &fddpi.pi_apps, NULL );
-				fddpi.pi_flags.persist = PI_PRST_ALWAYS;
 			}
 			CONNPEER '=' QSTRING peerinfo ';'
 			{
@@ -327,7 +326,8 @@
 					
 				/* Now destroy any content in the structure */
 				free(fddpi.pi_diamid);
-				free(fddpi.pi_sec_data.priority);
+				free(fddpi.config.pic_realm);
+				free(fddpi.config.pic_priority);
 				while (!FD_IS_LIST_EMPTY(&fddpi.pi_endpoints)) {
 					struct fd_list * li = fddpi.pi_endpoints.next;
 					fd_list_unlink(li);
@@ -343,21 +343,21 @@
 peerparams:		/* empty */
 			| peerparams NOIP ';'
 			{
-				if ((conf->cnf_flags.no_ip6) || (fddpi.pi_flags.pro3 == PI_P3_IP)) { 
+				if ((conf->cnf_flags.no_ip6) || (fddpi.config.pic_flags.pro3 == PI_P3_IP)) { 
 					yyerror (&yylloc, conf, "No_IP conflicts with a No_IPv6 directive.");
 					YYERROR;
 				}
 				got_peer_noip++;
-				fddpi.pi_flags.pro3 = PI_P3_IPv6;
+				fddpi.config.pic_flags.pro3 = PI_P3_IPv6;
 			}
 			| peerparams NOIP6 ';'
 			{
-				if ((conf->cnf_flags.no_ip4) || (fddpi.pi_flags.pro3 == PI_P3_IPv6)) { 
+				if ((conf->cnf_flags.no_ip4) || (fddpi.config.pic_flags.pro3 == PI_P3_IPv6)) { 
 					yyerror (&yylloc, conf, "No_IPv6 conflicts with a No_IP directive.");
 					YYERROR;
 				}
 				got_peer_noipv6++;
-				fddpi.pi_flags.pro3 = PI_P3_IP;
+				fddpi.config.pic_flags.pro3 = PI_P3_IP;
 			}
 			| peerparams NOTCP ';'
 			{
@@ -365,59 +365,55 @@
 					yyerror (&yylloc, conf, "No_TCP cannot be specified in daemon compiled with DISABLE_SCTP option.");
 					YYERROR;
 				#endif
-				if ((conf->cnf_flags.no_sctp) || (fddpi.pi_flags.pro4 == PI_P4_TCP)) { 
+				if ((conf->cnf_flags.no_sctp) || (fddpi.config.pic_flags.pro4 == PI_P4_TCP)) { 
 					yyerror (&yylloc, conf, "No_TCP conflicts with a No_SCTP directive.");
 					YYERROR;
 				}
 				got_peer_notcp++;
-				fddpi.pi_flags.pro4 = PI_P4_SCTP;
+				fddpi.config.pic_flags.pro4 = PI_P4_SCTP;
 			}
 			| peerparams NOSCTP ';'
 			{
-				if ((conf->cnf_flags.no_tcp) || (fddpi.pi_flags.pro4 == PI_P4_SCTP)) { 
+				if ((conf->cnf_flags.no_tcp) || (fddpi.config.pic_flags.pro4 == PI_P4_SCTP)) { 
 					yyerror (&yylloc, conf, "No_SCTP conflicts with a No_TCP directive.");
 					YYERROR;
 				}
 				got_peer_nosctp++;
-				fddpi.pi_flags.pro4 = PI_P4_TCP;
+				fddpi.config.pic_flags.pro4 = PI_P4_TCP;
 			}
 			| peerparams PREFERTCP ';'
 			{
-				fddpi.pi_flags.alg = PI_ALGPREF_TCP;
+				fddpi.config.pic_flags.alg = PI_ALGPREF_TCP;
 			}
 			| peerparams OLDTLS ';'
 			{
-				if (fddpi.pi_flags.sec == PI_SEC_NONE) { 
-					yyerror (&yylloc, conf, "ConnectPeer: TLS_old_method conflicts with No_TLS.");
-					YYERROR;
-				}
-				fddpi.pi_flags.sec = PI_SEC_TLS_OLD;
+				fddpi.config.pic_flags.sec |= PI_SEC_TLS_OLD;
 			}
 			| peerparams NOTLS ';'
 			{
-				if (fddpi.pi_flags.sec == PI_SEC_TLS_OLD) { 
-					yyerror (&yylloc, conf, "ConnectPeer: No_TLS conflicts with TLS_old_method.");
-					YYERROR;
-				}
-				fddpi.pi_flags.sec = PI_SEC_NONE;
+				fddpi.config.pic_flags.sec |= PI_SEC_NONE;
+			}
+			| peerparams REALM '=' QSTRING ';'
+			{
+				fddpi.config.pic_realm = $4;
 			}
 			| peerparams PORT '=' INTEGER ';'
 			{
 				CHECK_PARAMS_DO( ($4 > 0) && ($4 < 1<<16),
-					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
-				fddpi.pi_port = (uint16_t)$4;
+					{ yyerror (&yylloc, conf, "Invalid port value"); YYERROR; } );
+				fddpi.config.pic_port = (uint16_t)$4;
 			}
 			| peerparams TCTIMER '=' INTEGER ';'
 			{
-				fddpi.pi_tctimer = $4;
+				fddpi.config.pic_tctimer = $4;
+			}
+			| peerparams TWTIMER '=' INTEGER ';'
+			{
+				fddpi.config.pic_twtimer = $4;
 			}
 			| peerparams TLS_PRIO '=' QSTRING ';'
 			{
-				fddpi.pi_sec_data.priority = $4;
-			}
-			| peerparams TWTIMER '=' INTEGER ';'
-			{
-				fddpi.pi_twtimer = $4;
+				fddpi.config.pic_priority = $4;
 			}
 			| peerparams CONNTO '=' QSTRING ';'
 			{
--- a/freeDiameter/p_ce.c	Mon Nov 02 17:31:36 2009 +0900
+++ b/freeDiameter/p_ce.c	Thu Nov 05 14:28:46 2009 +0900
@@ -46,7 +46,7 @@
 
 int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid)
 {
-	switch (peer->p_hdr.info.pi_state) {
+	switch (peer->p_hdr.info.runtime.pir_state) {
 		case STATE_CLOSED:
 			TODO("Handle the CER, validate the peer if needed (and set expiry), set the alt_fifo in the connection, reply a CEA, eventually handshake, move to OPEN or REOPEN state");
 			/* In case of error : DIAMETER_UNKNOWN_PEER */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/freeDiameter/p_cnx.c	Thu Nov 05 14:28:46 2009 +0900
@@ -0,0 +1,84 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, 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.								 *
+*********************************************************************************************************/
+
+#include "fD.h"
+
+/* This file contains code used by a peer state machine to initiate a connection to remote peer */
+
+
+/* The thread that attempts the connection */
+static void * connect_thr(void * arg)
+{
+	struct fd_peer * peer = arg;
+	struct cnxctx * cnx = NULL;
+	
+	
+	
+	/* Use the flags in the peer to select the protocol */
+	
+	TODO("loop on fd_cnx_cli_connect_tcp or fd_cnx_cli_connect_sctp");
+	
+	
+	/* Now, we have an established connection in cnx */
+	
+	pthread_cleanup_push((void *)fd_cnx_destroy, cnx);
+	
+	/* Handshake if needed (secure port) */
+	
+	
+	
+	/* Upon success, generate FDEVP_CNX_ESTABLISHED */
+	CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx), goto fatal_error );
+	pthread_cleanup_pop(0);
+	
+	return NULL;
+fatal_error:
+	/* Cleanup the connection */
+	fd_cnx_destroy(cnx);
+
+	/* Generate a termination event */
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	
+	return NULL;
+}
+
+
+/* Initiate a connection attempt to a remote peer */
+int fd_p_cnx_init(struct fd_peer * peer)
+{
+	/* Start the connect thread */
+	CHECK_FCT( pthread_create(&peer->p_ini_thr, NULL, connect_thr, peer) );
+	return 0;
+}
--- a/freeDiameter/p_expiry.c	Mon Nov 02 17:31:36 2009 +0900
+++ b/freeDiameter/p_expiry.c	Thu Nov 05 14:28:46 2009 +0900
@@ -60,10 +60,10 @@
 		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
 			struct fd_peer * peer = (struct fd_peer *)li;
 			
-			if (peer->p_hdr.info.pi_state != STATE_ZOMBIE)
+			if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE)
 				continue;
 			
-			if (peer->p_hdr.info.pi_flags.persist == PI_PRST_ALWAYS)
+			if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_ALWAYS)
 				continue; /* This peer was not supposed to terminate, keep it in the list for debug */
 			
 			/* Ok, the peer was expired, let's remove it */
@@ -157,13 +157,12 @@
 {
 	CHECK_FCT_DO( fd_thr_term(&exp_thr), );
 	CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
-	
 	while (!FD_IS_LIST_EMPTY(&exp_list)) {
 		struct fd_peer * peer = (struct fd_peer *)(exp_list.next->o);
 		fd_list_unlink(&peer->p_expiry );
 	}
+	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
 	
-	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
 	CHECK_FCT_DO( fd_thr_term(&gc_thr), );
 	return 0;
 }
@@ -179,12 +178,12 @@
 	fd_list_unlink(&peer->p_expiry );
 	
 	/* if peer expires */
-	if (peer->p_hdr.info.pi_flags.exp) {
+	if (peer->p_hdr.info.config.pic_flags.exp) {
 		struct fd_list * li;
 		
 		/* update the p_exp_timer value */
 		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer)  );
-		peer->p_exp_timer.tv_sec += peer->p_hdr.info.pi_lft;
+		peer->p_exp_timer.tv_sec += peer->p_hdr.info.config.pic_lft;
 		
 		/* add to the expiry list in appropriate position (probably around the end) */
 		for (li = exp_list.prev; li != &exp_list; li = li->prev) {
--- a/freeDiameter/p_out.c	Mon Nov 02 17:31:36 2009 +0900
+++ b/freeDiameter/p_out.c	Thu Nov 05 14:28:46 2009 +0900
@@ -139,7 +139,7 @@
 	TRACE_ENTRY("%p %p %p", msg, cnx, peer);
 	CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx)));
 	
-	if (peer && (peer->p_hdr.info.pi_state == STATE_OPEN)) {
+	if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
 		/* Normal case: just queue for the out thread to pick it up */
 		CHECK_FCT( fd_fifo_post(peer->p_tosend, msg) );
 		
--- 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;
 }
 
--- a/freeDiameter/peers.c	Mon Nov 02 17:31:36 2009 +0900
+++ b/freeDiameter/peers.c	Thu Nov 05 14:28:46 2009 +0900
@@ -68,15 +68,15 @@
 	
 	fd_list_init(&p->p_hdr.chain, p);
 	
-	fd_list_init(&p->p_hdr.info.pi_endpoints, NULL);
-	fd_list_init(&p->p_hdr.info.pi_apps, NULL);
+	fd_list_init(&p->p_hdr.info.pi_endpoints, p);
+	fd_list_init(&p->p_hdr.info.runtime.pir_apps, p);
 	
 	p->p_eyec = EYEC_PEER;
+	fd_list_init(&p->p_actives, p);
 	fd_list_init(&p->p_expiry, p);
-	fd_list_init(&p->p_actives, p);
+	CHECK_FCT( fd_fifo_new(&p->p_tosend) );
 	p->p_hbh = lrand48();
-	CHECK_FCT( fd_fifo_new(&p->p_events) );
-	CHECK_FCT( fd_fifo_new(&p->p_tosend) );
+	
 	fd_list_init(&p->p_sr.srs, p);
 	CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) );
 	
@@ -97,27 +97,17 @@
 	
 	/* Copy the informations from the parameters received */
 	CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) );
-	if (info->pi_realm) {
-		CHECK_MALLOC( p->p_hdr.info.pi_realm = strdup(info->pi_realm) );
+	
+	memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) );
+	/* Duplicate the strings if provided */
+	if (info->config.pic_realm) {
+		CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) );
+	}
+	if (info->config.pic_priority) {
+		CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_priority) );
 	}
 	
-	p->p_hdr.info.pi_flags.pro3 	= info->pi_flags.pro3;
-	p->p_hdr.info.pi_flags.pro4 	= info->pi_flags.pro4;
-	p->p_hdr.info.pi_flags.alg  	= info->pi_flags.alg;
-	p->p_hdr.info.pi_flags.sec  	= info->pi_flags.sec;
-	p->p_hdr.info.pi_flags.exp  	= info->pi_flags.exp;
-	p->p_hdr.info.pi_flags.persist 	= info->pi_flags.persist;
-	
-	p->p_hdr.info.pi_lft     	= info->pi_lft;
-	p->p_hdr.info.pi_port    	= info->pi_port;
-	p->p_hdr.info.pi_tctimer 	= info->pi_tctimer;
-	p->p_hdr.info.pi_twtimer 	= info->pi_twtimer;
-	
-	if (info->pi_sec_data.priority) {
-		CHECK_MALLOC( p->p_hdr.info.pi_sec_data.priority = strdup(info->pi_sec_data.priority) );
-	}
-	
-	/* Move the items from one list to the other */
+	/* Move the list of endpoints into the peer */
 	if (info->pi_endpoints.next)
 		while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
 			li = info->pi_endpoints.next;
@@ -125,7 +115,6 @@
 			fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
 		}
 	
-	
 	/* The internal data */
 	if (orig_dbg) {
 		CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
@@ -149,15 +138,15 @@
 	}
 	
 	/* We can insert the new peer object */
-	if (! ret) {
-		/* Update expiry list */
-		CHECK_FCT_DO( ret = fd_p_expi_update( p ), goto out );
-		
-		/* Insert the new element in the list */
-		fd_list_insert_before( li, &p->p_hdr.chain );
-	}
+	if (! ret)
+		do {
+			/* Update expiry list */
+			CHECK_FCT_DO( ret = fd_p_expi_update( p ), break );
 
-out:	
+			/* Insert the new element in the list */
+			fd_list_insert_before( li, &p->p_hdr.chain );
+		} while (0);
+
 	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
 	if (ret) {
 		CHECK_FCT( fd_peer_free(&p) );
@@ -202,7 +191,7 @@
 	return;
 }
 
-/* Destroy a structure once all cleanups have been performed */
+/* Destroy a structure once cleanups have been performed (fd_psm_abord, ...) */
 int fd_peer_free(struct fd_peer ** ptr)
 {
 	struct fd_peer *p;
@@ -216,33 +205,22 @@
 	
 	CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
 	
-	free_null(p->p_hdr.info.pi_diamid); 
-	free_null(p->p_hdr.info.pi_realm); 
+	free_null(p->p_hdr.info.pi_diamid);
+	
+	free_null(p->p_hdr.info.config.pic_realm); 
+	free_null(p->p_hdr.info.config.pic_priority); 
+	
+	free_null(p->p_hdr.info.runtime.pir_realm);
+	free_null(p->p_hdr.info.runtime.pir_prodname);
+	free_list( &p->p_hdr.info.runtime.pir_apps );
+	
 	free_list( &p->p_hdr.info.pi_endpoints );
-	TODO("Free the security data if any ?");
-	free_null(p->p_hdr.info.pi_prodname);
-	free_list( &p->p_hdr.info.pi_apps );
 	
 	free_null(p->p_dbgorig);
-	ASSERT(FD_IS_LIST_EMPTY(&p->p_expiry));
-	ASSERT(FD_IS_LIST_EMPTY(&p->p_actives));
 	
-	CHECK_FCT( fd_thr_term(&p->p_psm) );
-	while ( fd_fifo_tryget(p->p_events, &t) == 0 ) {
-		struct fd_event * ev = t;
-		TRACE_DEBUG(FULL, "Found event %d(%p) in queue of peer %p being destroyed", ev->code, ev->data, p);
-		free(ev);
-	}
-	CHECK_FCT( fd_fifo_del(&p->p_events) );
+	fd_list_unlink(&p->p_expiry);
+	fd_list_unlink(&p->p_actives);
 	
-	CHECK_FCT( fd_thr_term(&p->p_outthr) );
-	
-	if (p->p_cnxctx) {
-		fd_cnx_destroy(p->p_cnxctx);
-	}
-	
-	/* Requeue any remaining message into global structures if possible */
-	fd_peer_failover_msg(p);
 	CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
 	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
 	
@@ -255,7 +233,7 @@
 	return 0;
 }
 
-/* Terminate peer module (destroy all peers) */
+/* Terminate peer module (destroy all peers, first gently, then violently) */
 int fd_peer_fini()
 {
 	struct fd_list * li;
@@ -273,7 +251,7 @@
 	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
 		struct fd_peer * peer = (struct fd_peer *)li;
 		
-		if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
+		if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
 			CHECK_FCT_DO( fd_psm_terminate(peer), /* continue */ );
 		} else {
 			li = li->prev; /* to avoid breaking the loop */
@@ -286,8 +264,8 @@
 	
 	if (!list_empty) {
 		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
-		TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT);
-		wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT;
+		TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT + 1);
+		wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT + 1;
 		wait_until.tv_nsec = now.tv_nsec;
 	}
 	
@@ -300,7 +278,7 @@
 		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
 		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
 			struct fd_peer * peer = (struct fd_peer *)li;
-			if (peer->p_hdr.info.pi_state == STATE_ZOMBIE) {
+			if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) {
 				li = li->prev; /* to avoid breaking the loop */
 				fd_list_unlink(&peer->p_hdr.chain);
 				fd_list_insert_before(&purge, &peer->p_hdr.chain);
@@ -350,32 +328,28 @@
 		return;
 	}
 
-	fd_log_debug(">  %s\t%s", STATE_STR(peer->p_hdr.info.pi_state), peer->p_hdr.info.pi_diamid);
+	fd_log_debug(">  %s\t%s", STATE_STR(peer->p_hdr.info.runtime.pir_state), peer->p_hdr.info.pi_diamid);
 	if (details > INFO) {
-		fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.pi_realm);
-		if (peer->p_hdr.info.pi_prodname)
-			fd_log_debug("\t['%s' %u]", peer->p_hdr.info.pi_prodname, peer->p_hdr.info.pi_firmrev);
+		fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "(unknown)");
+		if (peer->p_hdr.info.runtime.pir_prodname)
+			fd_log_debug("\t['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev);
 	}
 	fd_log_debug("\n");
 	if (details > FULL) {
 		/* Dump all info */
-		fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig);
-		fd_log_debug("\tFlags : %s%s%s%s%s - %s%s%s\n", 
-				peer->p_hdr.info.pi_flags.pro3 == PI_P3_DEFAULT ? "" :
-					(peer->p_hdr.info.pi_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."),
-				peer->p_hdr.info.pi_flags.pro4 == PI_P4_DEFAULT ? "" :
-					(peer->p_hdr.info.pi_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."),
-				peer->p_hdr.info.pi_flags.alg ? "PrefTCP." : "",
-				peer->p_hdr.info.pi_flags.sec == PI_SEC_DEFAULT ? "" :
-					(peer->p_hdr.info.pi_flags.sec == PI_SEC_NONE ? "IPSec." : "InbandTLS."),
-				peer->p_hdr.info.pi_flags.exp ? "Expire." : "",
-				peer->p_hdr.info.pi_flags.inband_none ? "InbandIPsec." : "",
-				peer->p_hdr.info.pi_flags.inband_tls ?  "InbandTLS." : "",
-				peer->p_hdr.info.pi_flags.relay ? "Relay (0xffffff)" : "No relay"
+		fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig?: "not set");
+		fd_log_debug("\tConfig flags : %s%s%s%s%s - %s%s%s\n", 
+				peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_DEFAULT ? "" :
+					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."),
+				peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_DEFAULT ? "" :
+					(peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."),
+				peer->p_hdr.info.config.pic_flags.alg ? "PrefTCP." : "",
+				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE ? "NoTLSok" :"",
+				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD ? "OldTLS" :"",
+				peer->p_hdr.info.config.pic_flags.exp ? "Expire." : "",
+				peer->p_hdr.info.config.pic_flags.persist ? "Persist." : ""
 				);
-		fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.pi_lft);
-		
-		TODO("Dump remaining useful information");
+		fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.config.pic_lft);
 	}
 }
 
@@ -443,8 +417,8 @@
 		peer->p_flags.pf_responder = 1;
 		
 		/* Set this peer to expire on inactivity */
-		peer->p_hdr.info.pi_flags.exp 	= PI_EXP_INACTIVE;
-		peer->p_hdr.info.pi_lft		= 3600 * 3;	/* 3 hours without any message */
+		peer->p_hdr.info.config.pic_flags.exp 	= PI_EXP_INACTIVE;
+		peer->p_hdr.info.config.pic_lft		= 3600 * 3;	/* 3 hours without any message */
 		
 		/* Upgrade the lock to write lock */
 		CHECK_POSIX_DO( ret = pthread_rwlock_wrlock(&fd_g_peers_rw), goto out );
--- a/include/freeDiameter/freeDiameter.h	Mon Nov 02 17:31:36 2009 +0900
+++ b/include/freeDiameter/freeDiameter.h	Thu Nov 05 14:28:46 2009 +0900
@@ -276,68 +276,75 @@
 #define STATE_STR(state) \
 	(((unsigned)(state)) <= STATE_MAX ? peer_state_str[((unsigned)(state)) ] : "<Invalid>")
 
-/* Information about a remote peer. Same structure is used for creating a new entry, but not all fields are meaningful in that case */
+/* Information about a remote peer */
 struct peer_info {
 	
-	char * 		pi_diamid;	/* UTF-8, \0 terminated. The Diameter Identity of the remote peer */
-	char * 		pi_realm;	/* Its realm, as received in CER/CEA exchange. */
+	char * 		pi_diamid;	/* UTF-8, \0 terminated. The Diameter Identity of the remote peer. */
 	
 	struct {
-		#define PI_P3_DEFAULT	0	/* Use the default L3 protocol configured for the host */
-		#define PI_P3_IP	1	/* Use only IP to connect to this peer */
-		#define PI_P3_IPv6	2	/* resp, IPv6 */
-		unsigned	pro3 :2;
-		
-		#define PI_P4_DEFAULT	0	/* Use the default L4 proto configured for the host */
-		#define PI_P4_TCP	1	/* Only use TCP */
-		#define PI_P4_SCTP	2	/* Only use SCTP */
-		unsigned	pro4 :2;
-		
-		#define PI_ALGPREF_SCTP	0	/* SCTP is initially attempted */
-		#define PI_ALGPREF_TCP	1	/* TCP is initially attempted */
-		unsigned	alg :1;
-		
-		#define PI_SEC_DEFAULT	0	/* New TLS security (dedicated port protecting also CER/CEA) */
-		#define PI_SEC_NONE	1	/* Transparent security with this peer (IPsec) */
-		#define PI_SEC_TLS_OLD	2	/* Old TLS security (inband on default port) */
-		unsigned	sec :2;
+		struct {
+			#define PI_P3_DEFAULT	0	/* Use any available protocol */
+			#define PI_P3_IP	1	/* Use only IP to connect to this peer */
+			#define PI_P3_IPv6	2	/* resp, IPv6 */
+			unsigned	pro3 :2;
+
+			#define PI_P4_DEFAULT	0	/* Attempt any available protocol */
+			#define PI_P4_TCP	1	/* Only use TCP */
+			#define PI_P4_SCTP	2	/* Only use SCTP */
+			unsigned	pro4 :2;
+
+			#define PI_ALGPREF_SCTP	0	/* SCTP is  attempted first (default) */
+			#define PI_ALGPREF_TCP	1	/* TCP is attempted first */
+			unsigned	alg :1;
+
+			#define PI_SEC_DEFAULT	0	/* New TLS security (handshake after connection, protecting also CER/CEA) */
+			#define PI_SEC_NONE	1	/* Transparent security with this peer (IPsec) */
+			#define PI_SEC_TLS_OLD	2	/* Old TLS security (use Inband-Security-Id AVP during CER/CEA) */
+			unsigned	sec :2;		/* Set sec = 3 to authorize use of (Inband-Security-Id == NONE) with this peer, sec = 2 only authorizing TLS */
+
+			#define PI_EXP_NONE	0	/* the peer entry does not expire */
+			#define PI_EXP_INACTIVE	1	/* the peer entry expires (i.e. is deleted) after pi_lft seconds without activity */
+			unsigned	exp :1;
+
+			#define PI_PRST_NONE	0	/* the peer entry is deleted after disconnection / error */
+			#define PI_PRST_ALWAYS	1	/* the peer entry is persistant (will be kept as ZOMBIE in case of error) */
+			unsigned	persist :1;
+			
+		}		pic_flags;	/* Flags influencing the connection to the remote peer */
 		
-		#define PI_EXP_NONE	0	/* the peer entry does not expire */
-		#define PI_EXP_INACTIVE	1	/* the peer entry expires (i.e. is deleted) after pi_lft seconds without activity */
-		unsigned	exp :1;
+		char * 		pic_realm;	/* If configured, the daemon will match the received realm in CER/CEA matches this. */
+		uint16_t	pic_port; 	/* port to connect to. 0: default. */
+		
+		uint32_t 	pic_lft;	/* lifetime of this peer when inactive (see pic_flags.exp definition) */
+		int		pic_tctimer; 	/* use this value for TcTimer instead of global, if != 0 */
+		int		pic_twtimer; 	/* use this value for TwTimer instead of global, if != 0 */
 		
-		#define PI_PRST_NONE	0	/* the peer entry is deleted after disconnection / error */
-		#define PI_PRST_ALWAYS	1	/* the peer entry is persistant (will be kept as ZOMBIE in case of error) */
-		unsigned	persist :1;
+		char *		pic_priority;	/* Priority string for GnuTLS if we don't use the default */
+		
+	} config;	/* Configured data (static for this peer entry) */
+	
+	struct {
+		
+		enum peer_state	pir_state;	/* Current state of the peer in the state machine */
 		
-		unsigned	inband_none :1;	/* This is only meaningful with pi_flags.sec == 3 */
-		unsigned	inband_tls  :1;	/* This is only meaningful with pi_flags.sec == 3 */
+		char * 		pir_realm;	/* The received realm in CER/CEA. */
 		
-		unsigned	relay :1;	/* The remote peer advertized the relay application */
-
-	} 		pi_flags;	/* Some flags */
-	
-	/* Additional parameters */
-	uint32_t 	pi_lft;		/* lifetime of this peer when inactive (see pi_flags.exp definition) */
-	uint16_t	pi_port; 	/* port to connect to. 0: default. */
-	int		pi_tctimer; 	/* use this value for TcTimer instead of global, if != 0 */
-	int		pi_twtimer; 	/* use this value for TwTimer instead of global, if != 0 */
+		uint32_t	pir_vendorid;	/* Content of the Vendor-Id AVP, or 0 by default */
+		uint32_t	pir_orstate;	/* Origin-State-Id value */
+		char *		pir_prodname;	/* copy of UTF-8 Product-Name AVP (\0 terminated) */
+		uint32_t	pir_firmrev;	/* Content of the Firmware-Revision AVP */
+		int		pir_relay;	/* The remote peer advertized the relay application */
+		struct fd_list	pir_apps;	/* applications advertised by the remote peer, except relay (pi_flags.relay) */
+		
+		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 
+								   see there for example of using this data */
+		unsigned int 	pir_cert_list_size;		/* Number of certificates in the list */
+		
+	} runtime;	/* Data populated after connection, may change between 2 connections -- not used by fd_peer_add */
 	
 	struct fd_list	pi_endpoints;	/* Endpoint(s) of the remote peer (configured, discovered, or advertized). list of struct fd_endpoint. DNS resolved if empty. */
-	
-	/* The remaining information must not be modified, and is not used for peer creation */
-	enum peer_state	pi_state;
-	uint32_t	pi_vendorid;	/* Content of the Vendor-Id AVP, or 0 by default */
-	uint32_t	pi_orstate;	/* Origin-State-Id value */
-	char *		pi_prodname;	/* copy of UTF-8 Product-Name AVP (\0 terminated) */
-	uint32_t	pi_firmrev;	/* Content of the Firmware-Revision AVP */
-	struct fd_list	pi_apps;	/* applications advertised by the remote peer, except relay (pi_flags.relay) */
-	struct {
-		char			*priority;	/* In case the default priority is not appropriate */
-		/* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo see there for example of using this data */
-		const gnutls_datum_t 	*cert_list; 	/* The (valid) credentials that the peer has presented */
-		unsigned int 		 cert_list_size;/* Number of certificates in the list */
-	} 		pi_sec_data;
 };
 
 struct peer_hdr {
--- a/libfreeDiameter/messages.c	Mon Nov 02 17:31:36 2009 +0900
+++ b/libfreeDiameter/messages.c	Thu Nov 05 14:28:46 2009 +0900
@@ -1062,8 +1062,8 @@
 	CHECK_PARAMS_DO(  CHECK_MSG(msg),  return 0 /* pretend the message is not routable */ );
 	
 	if ( ! msg->msg_routable ) {
-		/* To define if a message is routable, we rely on the "PXY" command flag yet. */
-		msg->msg_routable = (msg->msg_public.msg_flags & CMD_FLAG_PROXIABLE) ? 1 : 2;
+		/* To define if a message is routable, we rely on the "PXY" flag (for application 0). */
+		msg->msg_routable = ((msg->msg_public.msg_appl != 0) || (msg->msg_public.msg_flags & CMD_FLAG_PROXIABLE)) ? 1 : 2;
 		
 		/* Note : the 'real' criteria according to the Diameter I-D is that the message is 
 		 routable if and only if the "Destination-Realm" AVP is required by the command ABNF.
"Welcome to our mercurial repository"