Mercurial > hg > waaad
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")