# HG changeset patch # User Sebastien Decugis # Date 1256890986 -32400 # Node ID 0e2b57789361eac0586e5498660f095777636021 # Parent e6fcdf12b9a06ddf45f36b1b32efbd580092f3dd Backup for the WE, some warnings remaining diff -r e6fcdf12b9a0 -r 0e2b57789361 freeDiameter/CMakeLists.txt --- a/freeDiameter/CMakeLists.txt Thu Oct 29 18:05:45 2009 +0900 +++ b/freeDiameter/CMakeLists.txt Fri Oct 30 17:23:06 2009 +0900 @@ -22,6 +22,7 @@ p_expiry.c p_out.c p_psm.c + p_sr.c server.c tcp.c ) diff -r e6fcdf12b9a0 -r 0e2b57789361 freeDiameter/fD.h --- a/freeDiameter/fD.h Thu Oct 29 18:05:45 2009 +0900 +++ b/freeDiameter/fD.h Fri Oct 30 17:23:06 2009 +0900 @@ -92,6 +92,12 @@ /* Create all the dictionary objects defined in the Diameter base RFC. */ int fd_dict_base_protocol(struct dictionary * dict); +/* Sentinel for the sent requests list */ +struct sr_list { + struct fd_list srs; + pthread_mutex_t mtx; +}; + /* Peers */ struct fd_peer { /* The "real" definition of the peer structure */ @@ -136,7 +142,7 @@ uint32_t p_hbh; /* Sent requests (for fallback), list of struct sentreq ordered by hbh */ - struct fd_list p_sentreq; + struct sr_list p_sr; /* connection context: socket and related information */ struct cnxctx *p_cnxctx; @@ -203,12 +209,6 @@ int validate; /* The peer is new, it must be validated (by an extension) or error CEA to be sent */ }; -/* Structure to store a sent request */ -struct sentreq { - struct fd_list chain; /* the "o" field points directly to the hop-by-hop of the request (uint32_t *) */ - struct msg *req; /* A request that was sent and not yet answered. */ -}; - /* Functions */ int fd_peer_fini(); @@ -219,6 +219,7 @@ int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx ); /* fd_peer_add declared in freeDiameter.h */ int fd_peer_validate( struct fd_peer * peer ); +void fd_peer_failover_msg(struct fd_peer * peer); /* Peer expiry */ int fd_p_expi_init(void); @@ -236,6 +237,11 @@ int fd_out_start(struct fd_peer * peer); int fd_out_stop(struct fd_peer * peer); +/* Peer sent requests cache */ +int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc); +int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req); +void fd_p_sr_failover(struct sr_list * srlist); + /* 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; extern pthread_rwlock_t fd_g_activ_peers_rw; /* protect the list */ diff -r e6fcdf12b9a0 -r 0e2b57789361 freeDiameter/messages.c --- a/freeDiameter/messages.c Thu Oct 29 18:05:45 2009 +0900 +++ b/freeDiameter/messages.c Fri Oct 30 17:23:06 2009 +0900 @@ -264,3 +264,43 @@ return 0; } +/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */ +int fd_msg_parse_or_error( struct msg ** msg ) +{ + int ret = 0; + struct msg * m; + struct msg_hdr * hdr = NULL; + struct fd_pei pei; + + TRACE_ENTRY("%p", msg); + + CHECK_PARAMS(msg && *msg); + m = *msg; + + /* Parse the message against our dictionary */ + ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei); + if (ret != EBADMSG) + return ret; + + fd_log_debug("The following message does not comply to the dictionary and rules (%s):\n", pei.pei_errcode); + fd_msg_dump_walk(NONE, m); + + /* Now create an answer error if the message is a query */ + CHECK_FCT( fd_msg_hdr(m, &hdr) ); + + if (hdr->msg_flags & CMD_FLAG_REQUEST) { + + /* Create the error message */ + CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) ); + + /* Set the error code */ + CHECK_FCT( fd_msg_rescode_set(*msg, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) ); + + } else { + /* Just discard */ + CHECK_FCT( fd_msg_free( m ) ); + *msg = NULL; + } + + return ret; +} diff -r e6fcdf12b9a0 -r 0e2b57789361 freeDiameter/p_out.c --- a/freeDiameter/p_out.c Thu Oct 29 18:05:45 2009 +0900 +++ b/freeDiameter/p_out.c Fri Oct 30 17:23:06 2009 +0900 @@ -36,29 +36,100 @@ #include "fD.h" /* Alloc a new hbh for requests, bufferize the message and send on the connection, save in sentreq if provided */ -static int do_send(struct msg ** msg, struct cnxctx * cnx, uint32_t * hbh, struct fd_list * sentreq) +static int do_send(struct msg ** msg, struct cnxctx * cnx, uint32_t * hbh, struct sr_list * srl) { - TRACE_ENTRY("%p %p %p %p", msg, cnx, hbh, sentreq); + struct msg_hdr * hdr; + int msg_is_a_req; + uint8_t * buf; + size_t sz; + int ret; + + TRACE_ENTRY("%p %p %p %p", msg, cnx, hbh, srl); + + /* Retrieve the message header */ + CHECK_FCT( fd_msg_hdr(*msg, &hdr) ); + + msg_is_a_req = (hdr->msg_flags & CMD_FLAG_REQUEST); + if (msg_is_a_req) { + CHECK_PARAMS(hbh && srl); + /* Alloc the hop-by-hop id and increment the value for next message */ + hdr->msg_hbhid = *hbh; + *hbh = hdr->msg_hbhid + 1; + } + + /* Create the message buffer */ + CHECK_FCT(fd_msg_bufferize( *msg, &buf, &sz )); - TODO("If message is a request"); - TODO("Alloc new *hbh"); + /* Send the message */ + pthread_cleanup_push( free, buf ); + CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz), { free(buf); return ret; } ); + pthread_cleanup_pop(1); - TODO("Bufferize the message, send it"); + /* Save a request */ + if (msg_is_a_req) { + CHECK_FCT_DO( fd_p_sr_store(srl, msg, &hdr->msg_hbhid), + { + fd_log_debug("The following request was sent successfully but not saved locally:\n" ); + fd_log_debug(" (as a result the matching answer will be discarded)\n" ); + fd_msg_dump_walk(NONE, *msg); + } ); + + } - TODO("Save in sentreq or free") + /* Free answers and unsaved requests */ + if (*msg) { + CHECK_FCT( fd_msg_free(*msg) ); + *msg = NULL; + } - return ENOTSUP; + return 0; +} + +static void cleanup_requeue(void * arg) +{ + struct msg *msg = arg; + CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &msg), + CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */)); } /* The code of the "out" thread */ static void * out_thr(void * arg) { - TODO("Pick next message in peer->p_tosend"); - TODO("do_send, log errors"); - TODO("In case of cancellation, requeue the message"); - return NULL; + struct fd_peer * peer = arg; + ASSERT( CHECK_PEER(peer) ); + + /* Set the thread name */ + { + char buf[48]; + sprintf(buf, "OUT/%.*s", sizeof(buf) - 5, peer->p_hdr.info.pi_diamid); + fd_log_threadname ( buf ); + } + + /* Loop until cancelation */ + while (1) { + struct msg * msg; + + /* Retrieve next message to send */ + CHECK_FCT_DO( fd_fifo_get(peer->p_tosend, &msg), goto error ); + + /* Now if we are cancelled, we requeue this message */ + pthread_cleanup_push(cleanup_requeue, msg); + + /* Send the message, log any error */ + CHECK_FCT_DO( do_send(&msg, peer->p_cnxctx, &peer->p_hbh, &peer->p_sr), + { + fd_log_debug("An error occurred while sending this message, it is lost:\n"); + fd_msg_dump_walk(NONE, msg); + fd_msg_free(msg); + } ); + + /* Loop */ + pthread_cleanup_pop(0); + } + error: - TODO(" Send an event to the peer "); + /* It is not really a connection error, but the effect is the same, we are not able to send anymore message */ + CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ ); return NULL; } @@ -83,7 +154,7 @@ cnx = peer->p_cnxctx; /* Do send the message */ - CHECK_FCT( do_send(msg, cnx, hbh, peer ? &peer->p_sentreq : NULL) ); + CHECK_FCT( do_send(msg, cnx, hbh, peer ? &peer->p_sr : NULL) ); } return 0; diff -r e6fcdf12b9a0 -r 0e2b57789361 freeDiameter/p_psm.c --- a/freeDiameter/p_psm.c Thu Oct 29 18:05:45 2009 +0900 +++ b/freeDiameter/p_psm.c Fri Oct 30 17:23:06 2009 +0900 @@ -83,32 +83,64 @@ /* Manage the list of active peers */ /************************************************************************/ - /* Enter/leave OPEN state */ static int enter_open_state(struct fd_peer * peer) { + struct fd_list * li; + CHECK_PARAMS( FD_IS_LIST_EMPTY(&peer->p_actives) ); + + /* Callback registered by the credential validator (fd_peer_validate_register) */ + 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.pi_state = STATE_CLOSING; + fd_psm_terminate(peer); + } ); + peer->p_cb2 = NULL; + return 0; + } + /* Insert in the active peers list */ CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) ); - TODO(" insert in fd_g_activ_peers "); + for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) { + struct fd_peer * next_p = (struct fd_peer *)li->o; + int cmp = strcmp(peer->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamid); + if (cmp < 0) + break; + } + fd_list_insert_before(li, &peer->p_actives); + CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) ); - CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) ); + /* Callback registered when the peer was added, by fd_peer_add */ + if (peer->p_cb) { + TRACE_DEBUG(FULL, "Calling add callback for peer %s", peer->p_hdr.info.pi_diamid); + (*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data); + peer->p_cb = NULL; + peer->p_cb_data = NULL; + } /* Start the thread to handle outgoing messages */ CHECK_FCT( fd_out_start(peer) ); - return ENOTSUP; + return 0; } static int leave_open_state(struct fd_peer * peer) { - TODO("Remove from active list"); + /* Remove from active peers list */ + CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) ); + fd_list_unlink( &peer->p_actives ); + CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) ); /* Stop the "out" thread */ CHECK_FCT( fd_out_stop(peer) ); - TODO("Failover pending messages: requeue in global structures"); + /* Failover the messages */ + fd_peer_failover_msg(peer); - return ENOTSUP; + return 0; } + /************************************************************************/ /* Helpers for state changes */ /************************************************************************/ @@ -164,7 +196,7 @@ peer->p_psm_timer.tv_sec += delay; -#if 0 +#ifdef SLOW_PSM /* temporary for debug */ peer->p_psm_timer.tv_sec += 10; #endif @@ -187,7 +219,7 @@ static void * p_psm_th( void * arg ) { struct fd_peer * peer = (struct fd_peer *)arg; - int created_started = started; + int created_started = started ? 1 : 0; int event; size_t ev_sz; void * ev_data; @@ -213,7 +245,7 @@ if (peer->p_flags.pf_responder) { psm_next_timeout(peer, 0, INCNX_TIMEOUT); } else { - psm_next_timeout(peer, created_started ? 0 : 1, 0); + psm_next_timeout(peer, created_started, 0); } psm_loop: @@ -237,15 +269,6 @@ goto psm_loop; } - /* Call the extension callback if needed */ - if (peer->p_cb) { - /* Check if we must call it */ - /* */ - /* OK */ - TODO("Call CB"); - TODO("Clear CB"); - } - /* Handle the (easy) debug event now */ if (event == FDEVP_DUMP_ALL) { fd_peer_dump(peer, ANNOYING); @@ -276,8 +299,66 @@ /* A message was received */ if (event == FDEVP_CNX_MSG_RECV) { - TODO("Parse the buffer into a message"); - /* parse_and_get_local_ccode */ + struct msg * msg = NULL; + struct msg_hdr * hdr; + + /* Parse the received buffer */ + CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg), + { + fd_log_debug("Received invalid data from peer '%s', closing the connection\n", peer->p_hdr.info.pi_diamid); + CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_end ); + goto psm_loop; + } ); + + TRACE_DEBUG(FULL, "Received this message from '%s':", peer->p_hdr.info.pi_diamid); + fd_msg_dump_walk(FULL, msg); + + /* Extract the header */ + CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end ); + + /* If it is an answer, associate with the request */ + 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_msg_dump_walk(NONE, msg); + fd_msg_free(msg); + goto psm_loop; + } + + /* Associate */ + CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end ); + } + + /* We received a valid message, update the expiry timer */ + CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end ); + + /* 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 { + /* 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) { + psm_next_timeout(peer, 1, peer->p_hdr.info.pi_twtimer ?: fd_g_config->cnf_timer_tw); + } + } + goto psm_loop; + } + + /* Link-local message: They must be understood by our dictionary */ + TODO("Check if it is a local message (CER, DWR, ...)"); TODO("If not, check we are in OPEN state"); TODO("Update expiry timer if needed"); @@ -318,6 +399,7 @@ switch (peer->p_hdr.info.pi_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 */ break; case STATE_WAITCNXACK: @@ -352,7 +434,7 @@ } goto psm_loop; - + psm_end: pthread_cleanup_pop(1); /* set STATE_ZOMBIE */ peer->p_psm = (pthread_t)NULL; @@ -397,12 +479,25 @@ void fd_psm_abord(struct fd_peer * peer ) { TRACE_ENTRY("%p", peer); - TODO("Cancel PSM thread"); - TODO("Cancel OUT thread"); - TODO("Cleanup the peer connection object"); - TODO("Cleanup the message queues (requeue)"); - TODO("Call p_cb with NULL parameter if needed"); + + /* 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); + } + + /* Failover the messages */ + fd_peer_failover_msg(peer); + + /* 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 */ return; } diff -r e6fcdf12b9a0 -r 0e2b57789361 freeDiameter/p_sr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freeDiameter/p_sr.c Fri Oct 30 17:23:06 2009 +0900 @@ -0,0 +1,145 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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" + +/* Structure to store a sent request */ +struct sentreq { + struct fd_list chain; /* the "o" field points directly to the hop-by-hop of the request (uint32_t *) */ + struct msg *req; /* A request that was sent and not yet answered. */ +}; + +/* Find an element in the list, or the following one */ +static struct fd_list * find_or_next(struct fd_list * srlist, uint32_t hbh, int * match) +{ + struct fd_list * li; + *match = 0; + for (li = srlist->next; li != srlist; li = li->next) { + uint32_t * nexthbh = li->o; + if (*nexthbh < hbh) + continue; + if (*nexthbh == hbh) + *match = 1; + break; + } + return li; +} + +/* Store a new sent request */ +int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc) +{ + struct sentreq * sr; + struct fd_list * next; + int match; + + TRACE_ENTRY("%p %p %p", srlist, req, hbhloc); + CHECK_PARAMS(srlist && req && *req && hbhloc); + + CHECK_MALLOC( sr = malloc(sizeof(struct sentreq)) ); + memset(sr, 0, sizeof(struct sentreq)); + fd_list_init(&sr->chain, hbhloc); + sr->req = *req; + + /* Search the place in the list */ + CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) ); + next = find_or_next(&srlist->srs, *hbhloc, &match); + if (match) { + TRACE_DEBUG(INFO, "A request with the same hop-by-hop Id was already sent: error"); + free(sr); + CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* ignore */ ); + return EINVAL; + } + + /* Save in the list */ + *req = NULL; + fd_list_insert_before(next, &sr->chain); + CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) ); + return 0; +} + +/* Fetch a request by hbh */ +int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req) +{ + struct sentreq * sr; + int match; + + TRACE_ENTRY("%p %x %p", srlist, hbh, req); + CHECK_PARAMS(srlist && req); + + /* Search the request in the list */ + CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) ); + sr = (struct sentreq *)find_or_next(&srlist->srs, hbh, &match); + if (!match) { + TRACE_DEBUG(INFO, "There is no saved request with this hop-by-hop id"); + *req = NULL; + } else { + /* Unlink */ + fd_list_unlink(&sr->chain); + *req = sr->req; + free(sr); + } + CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) ); + + /* Done */ + return 0; +} + +/* Failover requests (free or requeue routables) */ +void fd_p_sr_failover(struct sr_list * srlist) +{ + CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx), /* continue anyway */ ); + while (!FD_IS_LIST_EMPTY(&srlist->srs)) { + struct sentreq * sr = (struct sentreq *)(srlist->srs.next); + fd_list_unlink(&sr->chain); + if (fd_msg_is_routable(sr->req)) { + struct msg_hdr * hdr = NULL; + + /* Set the 'T' flag */ + CHECK_FCT_DO(fd_msg_hdr(sr->req, &hdr), /* continue */); + if (hdr) + hdr->msg_flags |= CMD_FLAG_RETRANSMIT; + + /* Requeue for sending to another peer */ + CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &sr->req), + CHECK_FCT_DO(fd_msg_free(sr->req), /* What can we do more? */)); + } else { + /* Just free the request... */ + CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */); + } + free(sr); + } + CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* continue anyway */ ); +} + diff -r e6fcdf12b9a0 -r 0e2b57789361 freeDiameter/peers.c --- a/freeDiameter/peers.c Thu Oct 29 18:05:45 2009 +0900 +++ b/freeDiameter/peers.c Fri Oct 30 17:23:06 2009 +0900 @@ -77,7 +77,8 @@ 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_sentreq, p); + fd_list_init(&p->p_sr.srs, p); + CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) ); return 0; } @@ -179,6 +180,27 @@ free(__li); \ } +/* Empty the lists of p_tosend and p_sentreq messages */ +void fd_peer_failover_msg(struct fd_peer * peer) +{ + struct msg *m; + TRACE_ENTRY("%p", peer); + CHECK_PARAMS_DO(CHECK_PEER(peer), return); + + /* Requeue all messages in the "out" queue */ + while ( fd_fifo_tryget(peer->p_tosend, &m) == 0 ) { + CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &m), + /* fallback: destroy the message */ + CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)); + } + + /* Requeue all routable sent requests */ + fd_p_sr_failover(&peer->p_sr); + + /* Done */ + return; +} + /* Destroy a structure once all cleanups have been performed */ int fd_peer_free(struct fd_peer ** ptr) { @@ -196,7 +218,7 @@ free_null(p->p_hdr.info.pi_diamid); free_null(p->p_hdr.info.pi_realm); free_list( &p->p_hdr.info.pi_endpoints ); - /* Assume the security data is already freed */ + TODO("Free the security data if any ?"); free_null(p->p_hdr.info.pi_prodname); free_list( &p->p_hdr.info.pi_apps ); @@ -213,31 +235,22 @@ CHECK_FCT( fd_fifo_del(&p->p_events) ); CHECK_FCT( fd_thr_term(&p->p_outthr) ); - while ( fd_fifo_tryget(p->p_tosend, &t) == 0 ) { - struct msg * m = t; - TRACE_DEBUG(FULL, "Found message %p in outgoing queue of peer %p being destroyed, requeue", m, p); - /* We simply requeue in global, the routing thread will re-handle it. */ - CHECK_FCT(fd_fifo_post(fd_g_outgoing, &m)); - } - CHECK_FCT( fd_fifo_del(&p->p_tosend) ); - - while (!FD_IS_LIST_EMPTY(&p->p_sentreq)) { - struct sentreq * sr = (struct sentreq *)(p->p_sentreq.next); - fd_list_unlink(&sr->chain); - TRACE_DEBUG(FULL, "Found message %p in list of sent requests to peer %p being destroyed, requeue (fallback)", sr->req, p); - CHECK_FCT(fd_fifo_post(fd_g_outgoing, &sr->req)); - free(sr); - } 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 */); + + /* If the callback is still around... */ if (p->p_cb) (*p->p_cb)(NULL, p->p_cb_data); + /* Free the structure */ free(p); - return 0; } diff -r e6fcdf12b9a0 -r 0e2b57789361 include/freeDiameter/freeDiameter.h --- a/include/freeDiameter/freeDiameter.h Thu Oct 29 18:05:45 2009 +0900 +++ b/include/freeDiameter/freeDiameter.h Fri Oct 30 17:23:06 2009 +0900 @@ -491,6 +491,8 @@ /* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */ int fd_msg_add_origin ( struct msg * msg, int osi ); +/* Parse a message against our dictionary, and in case of error log and eventually build the error reply (on return and EBADMSG, *msg == NULL or *msg is the error message ready to send) */ +int fd_msg_parse_or_error( struct msg ** msg ); /***************************************/ diff -r e6fcdf12b9a0 -r 0e2b57789361 include/freeDiameter/libfreeDiameter.h --- a/include/freeDiameter/libfreeDiameter.h Thu Oct 29 18:05:45 2009 +0900 +++ b/include/freeDiameter/libfreeDiameter.h Fri Oct 30 17:23:06 2009 +0900 @@ -1989,8 +1989,8 @@ * 0 : Operation complete. * !0 : an error occurred. */ -int fd_msg_source_set( struct msg * msg, char * diamid, uint32_t hash, int add_rr, struct dictionary * dict ); -int fd_msg_source_get( struct msg * msg, char ** diamid, uint32_t *hash ); +int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict ); +int fd_msg_source_get( struct msg * msg, char ** diamid ); /* * FUNCTION: fd_msg_eteid_get @@ -2137,13 +2137,21 @@ */ int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict ); +/* Parsing Error Information structure */ +struct fd_pei { + char * pei_errcode; /* name of the error code to use */ + struct avp * pei_avp; /* pointer to invalid or missing AVP (to be freed) */ + char * pei_message; /* Overwrite default message if needed */ + int pei_protoerr; /* do we set the 'E' bit in the error message ? */ +}; + /* * FUNCTION: fd_msg_parse_rules * * PARAMETERS: * object : A msg or grouped avp object that must be verified. * dict : The dictionary containing the rules definitions. - * rule : If not NULL, the first conflicting rule will be saved here if a conflict is found. + * error_info : If not NULL, the first problem information will be saved here. * * DESCRIPTION: * Check that the children of the object do not conflict with the dictionary rules (ABNF compliance). @@ -2154,7 +2162,8 @@ * EINVAL : The msg or avp object is invalid for this operation. * ENOMEM : Unable to allocate enough memory to complete the operation. */ -int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct dict_object ** rule); +int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info); + /* diff -r e6fcdf12b9a0 -r 0e2b57789361 libfreeDiameter/messages.c --- a/libfreeDiameter/messages.c Thu Oct 29 18:05:45 2009 +0900 +++ b/libfreeDiameter/messages.c Fri Oct 30 17:23:06 2009 +0900 @@ -123,7 +123,6 @@ void * data; } msg_cb; /* Callback to be called when an answer is received, if not NULL */ char * msg_src_id; /* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */ - uint32_t msg_src_hash; /* Hash of the msg_src_id value */ }; /* Macro to compute the message header size */ @@ -667,8 +666,8 @@ msg->msg_public.msg_hbhid, msg->msg_public.msg_eteid ); - fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p h:%x src:%s\n", - INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_src_hash, msg->msg_src_id?:"(nil)"); + fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p src:%s\n", + INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_src_id?:"(nil)"); } #define DUMP_VALUE(_format, _parms...) fd_log_debug(INOBJHDR "value : t:'%s' v:'" _format "'\n", INOBJHDRVAL, typename, ## _parms); @@ -1076,9 +1075,9 @@ } /* Associate source peer */ -int fd_msg_source_set( struct msg * msg, char * diamid, uint32_t hash, int add_rr, struct dictionary * dict ) +int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict ) { - TRACE_ENTRY( "%p %p %x %d %p", msg, diamid, hash, add_rr, dict); + TRACE_ENTRY( "%p %p %d %p", msg, diamid, add_rr, dict); /* Check we received a valid message */ CHECK_PARAMS( CHECK_MSG(msg) && dict ); @@ -1088,13 +1087,11 @@ /* If the request is to cleanup the source, we are done */ if (diamid == NULL) { - msg->msg_src_hash = 0; return 0; } /* Otherwise save the new informations */ CHECK_MALLOC( msg->msg_src_id = strdup(diamid) ); - msg->msg_src_hash = hash; if (add_rr) { struct dict_object *avp_rr_model; @@ -1122,9 +1119,9 @@ return 0; } -int fd_msg_source_get( struct msg * msg, char ** diamid, uint32_t *hash ) +int fd_msg_source_get( struct msg * msg, char ** diamid ) { - TRACE_ENTRY( "%p %p %p", msg, diamid, hash); + TRACE_ENTRY( "%p %p", msg, diamid); /* Check we received valid parameters */ CHECK_PARAMS( CHECK_MSG(msg) ); @@ -1132,8 +1129,6 @@ /* Copy the informations */ *diamid = msg->msg_src_id; - if (hash) - *hash = msg->msg_src_hash; /* done */ return 0; @@ -1862,24 +1857,32 @@ /* We use this structure as parameter for the next function */ struct parserules_data { - struct fd_list * sentinel; /* Sentinel of the list of children AVP */ - struct dict_object * ruleavp; /* If the rule conflicts, save the rule_avp here (we don't have direct access to the rule but it can be searched) */ + struct fd_list * sentinel; /* Sentinel of the list of children AVP */ + struct fd_pei * pei; /* If the rule conflicts, save the error here */ }; +/* Create an empty AVP of a given model (to use in Failed-AVP) */ +static struct avp * empty_avp(struct dict_object * model_avp) +{ + TODO("Create the AVP instance and set a 0 value"); + return NULL; +} + /* Check that a list of AVPs is compliant with a given rule -- will be iterated on the list of rules */ static int parserules_check_one_rule(void * data, struct dict_rule_data *rule) { - int ret = 0, count, first, last, min; - struct parserules_data * pr_data = (struct parserules_data *) data; + int count, first, last, min; + struct parserules_data * pr_data = data; TRACE_ENTRY("%p %p", data, rule); - /* Get statistics of the AVP concerned by this rule in the message instance */ + /* Get statistics of the AVP concerned by this rule in the parent instance */ parserules_stat_avps( rule->rule_avp, pr_data->sentinel, &count, &first, &last); if (TRACE_BOOL(ANNOYING)) { struct dict_avp_data avpdata; + int ret; ret = fd_dict_getval(rule->rule_avp, &avpdata); TRACE_DEBUG(ANNOYING, "Checking rule: p:%d(%d) m/M:%2d/%2d. Counted %d (first: %d, last:%d) of AVP '%s'", @@ -1895,7 +1898,6 @@ } /* Now check the rule is not conflicting */ - ret = 0; /* Check the "min" value */ if ((min = rule->rule_min) == -1) { @@ -1906,15 +1908,24 @@ } if (count < min) { TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is < the rule min (%d).", count, min); - ret = EBADMSG; - goto end; + if (pr_data->pei) { + pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP"; + pr_data->pei->pei_avp = empty_avp(rule->rule_avp); + } + return EBADMSG; } /* Check the "max" value */ if ((rule->rule_max != -1) && (count > rule->rule_max)) { TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is > the rule max (%d).", count, rule->rule_max); - ret = EBADMSG; - goto end; + if (pr_data->pei) { + if (rule->rule_max == 0) + pr_data->pei->pei_errcode = "DIAMETER_AVP_NOT_ALLOWED"; + else + pr_data->pei->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; + pr_data->pei->pei_avp = empty_avp(rule->rule_avp); /* Well we are supposed to return the (max + 1)th instance of the AVP instead... Pfff... */ TODO("Improve..."); + } + return EBADMSG; } /* Check the position and order (if relevant) */ @@ -1928,8 +1939,12 @@ /* Since "0*1" is a valid rule specifier, we only reject cases where the AVP appears *after* its fixed position */ if (first > rule->rule_order) { TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_HEAD AVP appears first in (%d) position, the rule requires (%d).", first, rule->rule_order); - ret = EBADMSG; - goto end; + if (pr_data->pei) { + pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP"; + pr_data->pei->pei_message = "AVP was not in its fixed position"; + pr_data->pei->pei_avp = empty_avp(rule->rule_avp); + } + return EBADMSG; } break; @@ -1937,34 +1952,32 @@ /* Since "0*1" is a valid rule specifier, we only reject cases where the AVP appears *before* its fixed position */ if (last > rule->rule_order) { /* We have a ">" here because we count in reverse order (i.e. from the end) */ TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_TAIL AVP appears last in (%d) position, the rule requires (%d).", last, rule->rule_order); - ret = EBADMSG; - goto end; + if (pr_data->pei) { + pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP"; + pr_data->pei->pei_message = "AVP was not in its fixed position"; + pr_data->pei->pei_avp = empty_avp(rule->rule_avp); + } + return EBADMSG; } break; default: /* What is this position ??? */ ASSERT(0); - ret = ENOTSUP; + return ENOTSUP; } /* We've checked all the parameters */ -end: - if (ret == EBADMSG) { - pr_data->ruleavp = rule->rule_avp; - } - - return ret; + return 0; } /* Check the rules recursively */ -static int parserules_do ( struct dictionary * dict, msg_or_avp * object, struct dict_object ** conflict_rule, int mandatory) +static int parserules_do ( struct dictionary * dict, msg_or_avp * object, struct fd_pei *error_info, int mandatory) { - int ret = 0; struct parserules_data data; struct dict_object * model = NULL; - TRACE_ENTRY("%p %p %p %d", dict, object, conflict_rule, mandatory); + TRACE_ENTRY("%p %p %p %d", dict, object, error_info, mandatory); /* object has already been checked and dict-parsed when we are called. */ @@ -1980,6 +1993,10 @@ /* Commands MUST be supported in the dictionary */ if (model == NULL) { TRACE_DEBUG(INFO, "Message with no dictionary model. EBADMSG"); + if (error_info) { + error_info->pei_errcode = "DIAMETER_COMMAND_UNSUPPORTED"; + error_info->pei_protoerr = 1; + } return EBADMSG; } } @@ -1989,6 +2006,10 @@ if ( mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) { /* Return an error in this case */ TRACE_DEBUG(INFO, "Mandatory AVP with no dictionary model. EBADMSG"); + if (error_info) { + error_info->pei_errcode = "DIAMETER_AVP_UNSUPPORTED"; + error_info->pei_avp = object; + } return EBADMSG; } else { /* We don't know any rule for this object, so assume OK */ @@ -2018,38 +2039,30 @@ || (mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) ) is_child_mand = 1; for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) { - CHECK_FCT( parserules_do ( dict, _C(ch->o), conflict_rule, is_child_mand ) ); + CHECK_FCT( parserules_do ( dict, _C(ch->o), error_info, is_child_mand ) ); } } /* Now check all rules of this object */ data.sentinel = &_C(object)->children; - data.ruleavp = NULL; - ret = fd_dict_iterate_rules ( model, &data, parserules_check_one_rule ); + data.pei = error_info; + CHECK_FCT( fd_dict_iterate_rules ( model, &data, parserules_check_one_rule ) ); - /* Save the reference to the eventual conflicting rule; otherwise set to NULL */ - if (conflict_rule && data.ruleavp) { - /* data.ruleavp contains the AVP, and model is the parent */ - struct dict_object * rule = NULL; - struct dict_rule_request req = { model, data.ruleavp }; - - CHECK_FCT_DO( fd_dict_search ( dict, DICT_RULE, RULE_BY_AVP_AND_PARENT, &req, &rule, ENOENT), rule = NULL ); - - *conflict_rule = rule; - } - - return ret; + return 0; } -int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct dict_object ** rule) +int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info) { - TRACE_ENTRY("%p %p", object, rule); + TRACE_ENTRY("%p %p %p", object, dict, error_info); /* Resolve the dictionary objects when missing. This also validates the object. */ CHECK_FCT( fd_msg_parse_dict ( object, dict ) ); + if (error_info) + memset(error_info, 0, sizeof(struct fd_pei)); + /* Call the recursive function */ - return parserules_do ( dict, object, rule, 1 ) ; + return parserules_do ( dict, object, error_info, 1 ) ; } /***************************************************************************************************************/