Commit 8cd9067c authored by Sebastien Decugis's avatar Sebastien Decugis
Browse files

Added code for DPR/DPA

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