# HG changeset patch # User Sebastien Decugis # Date 1254477426 -32400 # Node ID 013ce9851131ac68d47ca81c4bd809210ab3f68d # Parent 050f4f6f9f2adea282f18da627fe2c59fc399265 Started including TLS code diff -r 050f4f6f9f2a -r 013ce9851131 freeDiameter/CMakeLists.txt --- a/freeDiameter/CMakeLists.txt Fri Oct 02 17:46:14 2009 +0900 +++ b/freeDiameter/CMakeLists.txt Fri Oct 02 18:57:06 2009 +0900 @@ -26,6 +26,10 @@ fdd.tab.h ) +# Require GNU TLS for building the daemon +FIND_PACKAGE(GNUTLS REQUIRED) +INCLUDE_DIRECTORIES(${GNUTLS_INCLUDE_DIRS}) +SET(FD_LIBS ${FD_LIBS} ${GNUTLS_LIBRARIES}) # Building the executable ADD_EXECUTABLE(freeDiameterd ${FD_COMMON_SRC} ${FD_COMMON_GEN_SRC} main.c) diff -r 050f4f6f9f2a -r 013ce9851131 freeDiameter/fD.h --- a/freeDiameter/fD.h Fri Oct 02 17:46:14 2009 +0900 +++ b/freeDiameter/fD.h Fri Oct 02 18:57:06 2009 +0900 @@ -150,11 +150,11 @@ /* Events codespace for struct fd_peer->p_events */ enum { - /* request to terminate this peer : disconnect, requeue all messages */ - FDEVP_TERMINATE = 2000 + /* Dump all info about this peer in the debug log */ + FDEVP_DUMP_ALL = 2000 - /* Dump all info about this peer in the debug log */ - ,FDEVP_DUMP_ALL + /* request to terminate this peer : disconnect, requeue all messages */ + ,FDEVP_TERMINATE /* A message was received in the peer */ ,FDEVP_MSG_INCOMING @@ -163,6 +163,8 @@ ,FDEVP_PSM_TIMEOUT }; const char * fd_pev_str(int event); +#define CHECK_EVENT( _e ) \ + (((int)(_e) >= FDEVP_DUMP_ALL) && ((int)(_e) <= FDEVP_PSM_TIMEOUT)) /* Structure to store a sent request */ struct sentreq { @@ -173,6 +175,9 @@ /* Functions */ int fd_peer_fini(); void fd_peer_dump_list(int details); +void fd_peer_dump(struct fd_peer * peer, int details); +int fd_peer_alloc(struct fd_peer ** ptr); +int fd_peer_free(struct fd_peer ** ptr); /* fd_peer_add declared in freeDiameter.h */ /* Peer expiry */ diff -r 050f4f6f9f2a -r 013ce9851131 freeDiameter/main.c --- a/freeDiameter/main.c Fri Oct 02 17:46:14 2009 +0900 +++ b/freeDiameter/main.c Fri Oct 02 18:57:06 2009 +0900 @@ -37,6 +37,12 @@ #include #include +#include + +#ifdef GCRY_THREAD_OPTION_PTHREAD_IMPL +GCRY_THREAD_OPTION_PTHREAD_IMPL; +#endif /* GCRY_THREAD_OPTION_PTHREAD_IMPL */ + /* forward declarations */ static void * sig_hdl(void * arg); @@ -61,6 +67,7 @@ /* Initialize the library */ CHECK_FCT( fd_lib_init() ); + TRACE_DEBUG(INFO, "libfreeDiameter initialized."); /* Name this thread */ fd_log_threadname("Main"); @@ -71,6 +78,15 @@ /* Parse the command-line */ CHECK_FCT( main_cmdline(argc, argv) ); + /* Initialize gnutls */ + CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL ); + if ( ! gnutls_check_version(GNUTLS_VERSION) ) { + fprintf(stderr, "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'\n", gnutls_check_version(NULL)); + return EINVAL; + } else { + TRACE_DEBUG(INFO, "GNUTLS library '%s' initialized.", gnutls_check_version(NULL)); + } + /* Allow SIGINT and SIGTERM from this point */ CHECK_POSIX( pthread_create(&sig_th, NULL, sig_hdl, NULL) ); @@ -141,6 +157,8 @@ CHECK_FCT_DO( fd_thr_term(&sig_th), /* continue */ ); + gnutls_global_deinit(); + return ret; } @@ -167,21 +185,23 @@ { int c; int option_index = 0; + char * locale; struct option long_options[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { "config", 1, NULL, 'c' }, - { "debug", 0, NULL, 'd' }, - { "quiet", 0, NULL, 'q' }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "config", required_argument, NULL, 'c' }, + { "debug", no_argument, NULL, 'd' }, + { "quiet", no_argument, NULL, 'q' }, + { "dbglocale", optional_argument, NULL, 'l' }, + { NULL, 0, NULL, 0 } }; TRACE_ENTRY("%d %p", argc, argv); /* Loop on arguments */ while (1) { - c = getopt_long (argc, argv, "hVc:dq", long_options, &option_index); + c = getopt_long (argc, argv, "hVc:dql:", long_options, &option_index); if (c == -1) break; /* Exit from the loop. */ @@ -199,6 +219,16 @@ fd_g_config->cnf_file = optarg; break; + case 'l': /* Change the locale. */ + locale = setlocale(LC_ALL, optarg?:""); + if (locale) { + TRACE_DEBUG(INFO, "Locale set to: %s", optarg ?: locale); + } else { + TRACE_DEBUG(INFO, "Unable to set locale (%s)", optarg); + return EINVAL; + } + break; + case 'd': /* Increase verbosity of debug messages. */ fd_g_debug_lvl++; break; @@ -209,11 +239,11 @@ case '?': /* Invalid option. */ /* `getopt_long' already printed an error message. */ - TRACE_DEBUG(INFO, "getopt_long found an invalid character\n"); + TRACE_DEBUG(INFO, "getopt_long found an invalid character"); return EINVAL; default: /* bug: option not considered. */ - TRACE_DEBUG(INFO, "A command-line option is missing in parser: %c\n", c); + TRACE_DEBUG(INFO, "A command-line option is missing in parser: %c", c); ASSERT(0); return EINVAL; } @@ -266,6 +296,7 @@ " default location (%s).\n", DEFAULT_CONF_FILE); printf( "\nDebug:\n" " These options are mostly useful for developers\n" + " -l, --dbglocale Set the locale for error messages\n" " -d, --debug Increase verbosity of debug messages\n" " -q, --quiet Decrease verbosity then remove debug messages\n"); } diff -r 050f4f6f9f2a -r 013ce9851131 freeDiameter/p_expiry.c --- a/freeDiameter/p_expiry.c Fri Oct 02 17:46:14 2009 +0900 +++ b/freeDiameter/p_expiry.c Fri Oct 02 18:57:06 2009 +0900 @@ -35,11 +35,63 @@ #include "fD.h" +/* Delay for garbage collection of expired threads, in seconds */ +#define GC_TIME 60 + static pthread_t exp_thr; +static pthread_t gc_thr; static struct fd_list exp_list = FD_LIST_INITIALIZER( exp_list ); static pthread_cond_t exp_cnd = PTHREAD_COND_INITIALIZER; static pthread_mutex_t exp_mtx = PTHREAD_MUTEX_INITIALIZER; +static void * gc_th_fct(void * arg) +{ + fd_log_threadname ( "Peers/garbage" ); + TRACE_ENTRY( "" ); + + do { + struct fd_list * li, purge = FD_LIST_INITIALIZER(purge); + + pthread_testcancel(); + sleep(GC_TIME); + + /* Now check in the peers list if any peer can be deleted */ + CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), goto error ); + + for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { + struct fd_peer * peer = (struct fd_peer *)li; + + if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) + continue; + + if (peer->p_hdr.info.pi_flags.exp == PI_EXP_NONE) + continue; /* This peer was not supposed to expire, keep it in the list */ + + /* Ok, the peer was expired, let's remove it */ + li = li->prev; /* to avoid breaking the loop */ + fd_list_unlink(&peer->p_hdr.chain); + fd_list_insert_before(&purge, &peer->p_hdr.chain); + } + + CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), goto error ); + + /* Now delete peers that are in the purge list */ + while (!FD_IS_LIST_EMPTY(&purge)) { + struct fd_peer * peer = (struct fd_peer *)(purge.next); + fd_list_unlink(&peer->p_hdr.chain); + TRACE_DEBUG(INFO, "Garbage Collect: delete zombie peer '%s'", peer->p_hdr.info.pi_diamid); + CHECK_FCT_DO( fd_peer_free(&peer), /* Continue... what else to do ? */ ); + } + } while (1); + +error: + TRACE_DEBUG(INFO, "An error occurred in peers module! GC thread is terminating..."); + ASSERT(0); + CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), ); + return NULL; +} + + static void * exp_th_fct(void * arg) { fd_log_threadname ( "Peers/expire" ); @@ -97,6 +149,7 @@ { TRACE_ENTRY(); CHECK_FCT( pthread_create( &exp_thr, NULL, exp_th_fct, NULL ) ); + CHECK_FCT( pthread_create( &gc_thr, NULL, gc_th_fct, NULL ) ); return 0; } @@ -112,6 +165,7 @@ } CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) ); + CHECK_FCT_DO( fd_thr_term(&gc_thr), ); return 0; } diff -r 050f4f6f9f2a -r 013ce9851131 freeDiameter/p_psm.c --- a/freeDiameter/p_psm.c Fri Oct 02 17:46:14 2009 +0900 +++ b/freeDiameter/p_psm.c Fri Oct 02 18:57:06 2009 +0900 @@ -36,7 +36,7 @@ #include "fD.h" const char *peer_state_str[] = { - "STATE_ZOMBIE" + "STATE_NEW" , "STATE_OPEN" , "STATE_CLOSED" , "STATE_CLOSING" @@ -45,6 +45,7 @@ , "STATE_WAITCEA" , "STATE_SUSPECT" , "STATE_REOPEN" + , "STATE_ZOMBIE" }; const char * fd_pev_str(int event) @@ -122,6 +123,7 @@ #endif } +/* Wait for the next event in the PSM, or timeout */ static int psm_ev_timedget(struct fd_peer * peer, int *code, void ** data) { struct fd_event * ev; @@ -141,13 +143,15 @@ } return 0; -} +} -/* The state machine thread */ +/* The state machine thread (controler) */ static void * p_psm_th( void * arg ) { struct fd_peer * peer = (struct fd_peer *)arg; int created_started = started; + int event; + void * ev_data; CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) ); @@ -160,12 +164,12 @@ fd_log_threadname ( buf ); } - /* Wait that the PSM are authorized to start in the daemon */ - CHECK_FCT_DO( fd_psm_waitstart(), goto end ); - /* The state machine starts in CLOSED state */ peer->p_hdr.info.pi_state = STATE_CLOSED; + /* Wait that the PSM are authorized to start in the daemon */ + CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end ); + /* Initialize the timer */ if (peer->p_flags.pf_responder) { psm_next_timeout(peer, 0, INCNX_TIMEOUT); @@ -173,27 +177,68 @@ psm_next_timeout(peer, created_started ? 0 : 1, 0); } -psm: - do { - int event; - void * ev_data; - - /* Get next event */ - CHECK_FCT_DO( psm_ev_timedget(peer, &event, &ev_data), goto end ); - TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p)\t'%s'", - STATE_STR(peer->p_hdr.info.pi_state), - fd_pev_str(event), ev_data, - peer->p_hdr.info.pi_diamid); - - /* Now, the action depends on the current state and the incoming event */ - +psm_loop: + /* Get next event */ + CHECK_FCT_DO( psm_ev_timedget(peer, &event, &ev_data), goto psm_end ); + TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p)\t'%s'", + STATE_STR(peer->p_hdr.info.pi_state), + fd_pev_str(event), ev_data, + peer->p_hdr.info.pi_diamid); + + /* Now, the action depends on the current state and the incoming event */ + + /* The following two states are impossible */ + ASSERT( peer->p_hdr.info.pi_state != STATE_NEW ); + ASSERT( peer->p_hdr.info.pi_state != STATE_ZOMBIE ); + + /* Purge invalid events */ + if (!CHECK_EVENT(event)) { + TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event); + goto psm_loop; + } + + /* Handle the (easy) debug event now */ + if (event == FDEVP_DUMP_ALL) { + fd_peer_dump(peer, ANNOYING); + goto psm_loop; + } + + /* Requests to terminate the peer object */ + if (event == FDEVP_TERMINATE) { + switch (peer->p_hdr.info.pi_state) { + case STATE_CLOSING: + case STATE_WAITCNXACK: + case STATE_WAITCNXACK_ELEC: + case STATE_WAITCEA: + case STATE_SUSPECT: + /* In these cases, we just cleanup the peer object and terminate now */ + TODO("Cleanup the PSM: terminate connection object, ..."); + case STATE_CLOSED: + /* Then we just terminate the PSM */ + goto psm_end; + + case STATE_OPEN: + case STATE_REOPEN: + /* We cannot just close the conenction, we have to send a DPR first */ + TODO("Send DPR, mark the peer as CLOSING"); + goto psm_loop; + } + } - } while (1); - + /* MSG_RECEIVED: fd_p_expi_update(struct fd_peer * peer ) */ + /* If timeout or OPEN : call cb if defined */ + + /* Default action : the handling has not yet been implemented. */ + TODO("Missing handler in PSM : '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.pi_state), fd_pev_str(event)); + if (event == FDEVP_PSM_TIMEOUT) { + /* We have not handled timeout in this state, let's postpone next alert */ + psm_next_timeout(peer, 0, 60); + } -end: - /* set STATE_ZOMBIE */ - pthread_cleanup_pop(1); + goto psm_loop; + +psm_end: + pthread_cleanup_pop(1); /* set STATE_ZOMBIE */ return NULL; } @@ -204,8 +249,15 @@ int fd_psm_begin(struct fd_peer * peer ) { TRACE_ENTRY("%p", peer); - TODO(""); - return ENOTSUP; + + /* Check the peer and state are OK */ + CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.pi_state == STATE_NEW) ); + + /* Create the PSM controler thread */ + CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) ); + + /* We're done */ + return 0; } /* End the PSM (clean ending) */ @@ -213,7 +265,11 @@ { TRACE_ENTRY("%p", peer); CHECK_PARAMS( CHECK_PEER(peer) ); - CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, NULL) ); + if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) { + CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, NULL) ); + } else { + TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid); + } return 0; } @@ -224,7 +280,8 @@ TODO("Cancel PSM thread"); TODO("Cancel IN thread"); TODO("Cancel OUT thread"); - TODO("Cleanup the connection"); + TODO("Cleanup the peer connection object"); + TODO("Cleanup the message queues (requeue)"); return; } diff -r 050f4f6f9f2a -r 013ce9851131 freeDiameter/peers.c --- a/freeDiameter/peers.c Fri Oct 02 17:46:14 2009 +0900 +++ b/freeDiameter/peers.c Fri Oct 02 18:57:06 2009 +0900 @@ -35,63 +35,13 @@ #include "fD.h" +/* Global list of peers */ struct fd_list fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers); pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER; -/* Terminate peer module (destroy all peers) */ -int fd_peer_fini() -{ - struct fd_list * li; - TRACE_ENTRY(); - - CHECK_FCT_DO(fd_p_expi_fini(), /* continue */); - - TRACE_DEBUG(INFO, "Sending signal to terminate to all peer connections"); - - CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ ); - /* For each peer in the list, ... */ - for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - struct fd_peer * np = (struct fd_peer *)li; - CHECK_FCT_DO( fd_psm_terminate(np), /* continue */ ); - } - CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ ); - - TODO("Give some time to all PSM, then destroy remaining threads"); - /* fd_psm_abord(struct fd_peer * peer ) */ - - return 0; -} - -/* Dump the list of peers */ -void fd_peer_dump_list(int details) -{ - struct fd_list * li; - - fd_log_debug("Dumping list of peers :\n"); - CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ ); - - for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - struct fd_peer * np = (struct fd_peer *)li; - if (np->p_eyec != EYEC_PEER) { - fd_log_debug(" Invalid entry @%p !\n", li); - continue; - } - - fd_log_debug(" %s\t%s", STATE_STR(np->p_hdr.info.pi_state), np->p_hdr.info.pi_diamid); - if (details > INFO) { - fd_log_debug("\t(rlm:%s)", np->p_hdr.info.pi_realm); - if (np->p_hdr.info.pi_prodname) - fd_log_debug("\t['%s' %u]", np->p_hdr.info.pi_prodname, np->p_hdr.info.pi_firmrev); - fd_log_debug("\t(from %s)", np->p_dbgorig); - } - fd_log_debug("\n"); - } - - CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ ); -} /* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */ -static int fd_sp_reinit(struct fd_peer ** ptr) +int fd_peer_alloc(struct fd_peer ** ptr) { struct fd_peer *p; @@ -125,6 +75,86 @@ return 0; } +/* Add a new peer entry */ +int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data ) +{ + struct fd_peer *p = NULL; + struct fd_list * li; + int ret = 0; + TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data); + CHECK_PARAMS(info && info->pi_diamid); + + /* Create a structure to contain the new peer information */ + CHECK_FCT( fd_peer_alloc(&p) ); + + /* Copy the informations from the parameters received */ + CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) ); + if (info->pi_realm) { + CHECK_MALLOC( p->p_hdr.info.pi_realm = strdup(info->pi_realm) ); + } + + p->p_hdr.info.pi_flags.pro3 = info->pi_flags.pro3; + p->p_hdr.info.pi_flags.pro4 = info->pi_flags.pro4; + p->p_hdr.info.pi_flags.alg = info->pi_flags.alg; + p->p_hdr.info.pi_flags.sec = info->pi_flags.sec; + p->p_hdr.info.pi_flags.exp = info->pi_flags.exp; + + p->p_hdr.info.pi_lft = info->pi_lft; + p->p_hdr.info.pi_streams = info->pi_streams; + p->p_hdr.info.pi_port = info->pi_port; + p->p_hdr.info.pi_tctimer = info->pi_tctimer; + p->p_hdr.info.pi_twtimer = info->pi_twtimer; + + /* Move the items from one list to the other */ + if (info->pi_endpoints.next) + while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) { + li = info->pi_endpoints.next; + fd_list_unlink(li); + fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li); + } + + /* The internal data */ + if (orig_dbg) { + CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) ); + } else { + CHECK_MALLOC( p->p_dbgorig = strdup("unknown") ); + } + p->p_cb = cb; + p->p_cb_data = cb_data; + + /* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */ + CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) ); + + for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { + struct fd_peer * prev = (struct fd_peer *)li; + int cmp = strcasecmp( p->p_hdr.info.pi_diamid, prev->p_hdr.info.pi_diamid ); + if (cmp < 0) + continue; + if (cmp == 0) + ret = EEXIST; + break; + } + + /* We can insert the new peer object */ + if (! ret) { + /* Update expiry list */ + CHECK_FCT_DO( ret = fd_p_expi_update( p ), goto out ); + + /* Insert the new element in the list */ + fd_list_insert_before( li, &p->p_hdr.chain ); + } + +out: + CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) ); + if (ret) { + CHECK_FCT( fd_peer_free(&p) ); + } else { + CHECK_FCT( fd_psm_begin(p) ); + } + return ret; +} + + #define free_null( _v ) \ if (_v) { \ free(_v); \ @@ -139,7 +169,7 @@ } /* Destroy a structure once all cleanups have been performed */ -static int fd_sp_destroy(struct fd_peer ** ptr) +int fd_peer_free(struct fd_peer ** ptr) { struct fd_peer *p; void * t; @@ -209,85 +239,133 @@ return 0; } - -/* Add a new peer entry */ -int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data ) +/* Terminate peer module (destroy all peers) */ +int fd_peer_fini() { - struct fd_peer *p = NULL; struct fd_list * li; - int ret = 0; - TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data); - CHECK_PARAMS(info && info->pi_diamid); + struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */ + int list_empty; + struct timespec wait_until, now; + + TRACE_ENTRY(); + + CHECK_FCT_DO(fd_p_expi_fini(), /* continue */); + + TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections"); + + CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ ); + for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { + struct fd_peer * peer = (struct fd_peer *)li; + + if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) { + CHECK_FCT_DO( fd_psm_terminate(peer), /* continue */ ); + } else { + li = li->prev; /* to avoid breaking the loop */ + fd_list_unlink(&peer->p_hdr.chain); + fd_list_insert_before(&purge, &peer->p_hdr.chain); + } + } + list_empty = FD_IS_LIST_EMPTY(&fd_g_peers); + CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ ); - /* Create a structure to contain the new peer information */ - CHECK_FCT( fd_sp_reinit(&p) ); + if (!list_empty) { + CHECK_SYS( clock_gettime(CLOCK_REALTIME, &now) ); + TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT); + wait_until.tv_sec = now.tv_sec + DPR_TIMEOUT; + wait_until.tv_nsec = now.tv_nsec; + } - /* Copy the informations from the parameters received */ - CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) ); - if (info->pi_realm) { - CHECK_MALLOC( p->p_hdr.info.pi_realm = strdup(info->pi_realm) ); + while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) { + + /* Allow the PSM(s) to execute */ + pthread_yield(); + + /* Remove zombie peers */ + CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ ); + for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { + struct fd_peer * peer = (struct fd_peer *)li; + if (peer->p_hdr.info.pi_state == STATE_ZOMBIE) { + li = li->prev; /* to avoid breaking the loop */ + fd_list_unlink(&peer->p_hdr.chain); + fd_list_insert_before(&purge, &peer->p_hdr.chain); + } + } + list_empty = FD_IS_LIST_EMPTY(&fd_g_peers); + CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ ); } - p->p_hdr.info.pi_flags.pro3 = info->pi_flags.pro3; - p->p_hdr.info.pi_flags.pro4 = info->pi_flags.pro4; - p->p_hdr.info.pi_flags.alg = info->pi_flags.alg; - p->p_hdr.info.pi_flags.sec = info->pi_flags.sec; - p->p_hdr.info.pi_flags.exp = info->pi_flags.exp; + if (!list_empty) { + TRACE_DEBUG(INFO, "Forcing connections shutdown"); + CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ ); + while (!FD_IS_LIST_EMPTY(&fd_g_peers)) { + struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next); + fd_psm_abord(peer); + fd_list_unlink(&peer->p_hdr.chain); + fd_list_insert_before(&purge, &peer->p_hdr.chain); + } + CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ ); + } - p->p_hdr.info.pi_lft = info->pi_lft; - p->p_hdr.info.pi_streams = info->pi_streams; - p->p_hdr.info.pi_port = info->pi_port; - p->p_hdr.info.pi_tctimer = info->pi_tctimer; - p->p_hdr.info.pi_twtimer = info->pi_twtimer; + /* Free memory objects of all peers */ + while (!FD_IS_LIST_EMPTY(&purge)) { + struct fd_peer * peer = (struct fd_peer *)(purge.next); + fd_list_unlink(&peer->p_hdr.chain); + fd_peer_free(&peer); + } - /* Move the items from one list to the other */ - if (info->pi_endpoints.next) - while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) { - li = info->pi_endpoints.next; - fd_list_unlink(li); - fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li); - } - - p->p_hdr.info.pi_sec_module = info->pi_sec_module; - memcpy(&p->p_hdr.info.pi_sec_data, &info->pi_sec_data, sizeof(info->pi_sec_data)); + return 0; +} + +/* Dump info of one peer */ +void fd_peer_dump(struct fd_peer * peer, int details) +{ + if (peer->p_eyec != EYEC_PEER) { + fd_log_debug(" Invalid peer @ %p !\n", peer); + return; + } + + fd_log_debug("> %s\t%s", STATE_STR(peer->p_hdr.info.pi_state), peer->p_hdr.info.pi_diamid); + if (details > INFO) { + fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.pi_realm); + if (peer->p_hdr.info.pi_prodname) + fd_log_debug("\t['%s' %u]", peer->p_hdr.info.pi_prodname, peer->p_hdr.info.pi_firmrev); + } + fd_log_debug("\n"); + if (details > FULL) { + /* Dump all info */ + fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig); + fd_log_debug("\tFlags : %s%s%s%s%s - %s%s%s\n", + peer->p_hdr.info.pi_flags.pro3 == PI_P3_DEFAULT ? "" : + (peer->p_hdr.info.pi_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."), + peer->p_hdr.info.pi_flags.pro4 == PI_P4_DEFAULT ? "" : + (peer->p_hdr.info.pi_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."), + peer->p_hdr.info.pi_flags.alg ? "PrefTCP." : "", + peer->p_hdr.info.pi_flags.sec == PI_SEC_DEFAULT ? "" : + (peer->p_hdr.info.pi_flags.sec == PI_SEC_NONE ? "IPSec." : "InbandTLS."), + peer->p_hdr.info.pi_flags.exp ? "Expire." : "", + peer->p_hdr.info.pi_flags.inband & PI_INB_NONE ? "InbandIPsecOK." : "", + peer->p_hdr.info.pi_flags.inband & PI_INB_TLS ? "InbandTLSOK." : "", + peer->p_hdr.info.pi_flags.relay ? "Relay (0xffffff)" : "No relay" + ); + fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.pi_lft); + + TODO("Dump remaining useful information"); + } +} + +/* Dump the list of peers */ +void fd_peer_dump_list(int details) +{ + struct fd_list * li; - /* The internal data */ - if (orig_dbg) { - CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) ); - } else { - CHECK_MALLOC( p->p_dbgorig = strdup("unknown") ); - } - p->p_cb = cb; - p->p_cb_data = cb_data; - - /* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */ - CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) ); + fd_log_debug("Dumping list of peers :\n"); + CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - struct fd_peer * prev = (struct fd_peer *)li; - int cmp = strcasecmp( p->p_hdr.info.pi_diamid, prev->p_hdr.info.pi_diamid ); - if (cmp < 0) - continue; - if (cmp == 0) - ret = EEXIST; - break; + struct fd_peer * np = (struct fd_peer *)li; + fd_peer_dump(np, details); } - /* We can insert the new peer object */ - if (! ret) { - /* Update expiry list */ - CHECK_FCT_DO( ret = fd_p_expi_update( p ), goto out ); - - /* Insert the new element in the list */ - fd_list_insert_before( li, &p->p_hdr.chain ); - } + CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ ); +} -out: - CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) ); - if (ret) { - CHECK_FCT( fd_sp_destroy(&p) ); - } else { - CHECK_FCT( fd_psm_begin(p) ); - } - return ret; -} diff -r 050f4f6f9f2a -r 013ce9851131 include/freeDiameter/freeDiameter.h --- a/include/freeDiameter/freeDiameter.h Fri Oct 02 17:46:14 2009 +0900 +++ b/include/freeDiameter/freeDiameter.h Fri Oct 02 18:57:06 2009 +0900 @@ -38,6 +38,24 @@ #include +#include +#include + +/* GNUTLS version */ +#ifndef GNUTLS_VERSION +#define GNUTLS_VERSION LIBGNUTLS_VERSION +#endif /* GNUTLS_VERSION */ + +/* Check the return value of a GNUTLS function, log and propagate */ +#define CHECK_GNUTLS_DO( __call__, __fallback__ ) { \ + int __ret__; \ + TRACE_DEBUG_ALL( "Check FCT: " #__call__ ); \ + __ret__ = (__call__); \ + if (__ret__ < 0) { \ + TRACE_DEBUG(INFO, "Error in '" #__call__ "':\t%s", gnutls_strerror(__ret__)); \ + __fallback__; \ + } \ +} /* Structure to hold the configuration of the freeDiameter daemon */ @@ -70,6 +88,11 @@ unsigned tls_alg: 1; /* TLS algorithm for initiated cnx. 0: separate port. 1: inband-security (old) */ } cnf_flags; + struct { + /* GNUTLS global state */ + /* Server credential(s) */ + } cnf_sec_data; + uint32_t cnf_orstateid; /* The value to use in Origin-State-Id, default to random value */ struct dictionary *cnf_dict; /* pointer to the global dictionary */ struct fifo *cnf_main_ev; /* events for the daemon's main (struct fd_event items) */ @@ -150,7 +173,7 @@ /* States of a peer */ enum peer_state { /* Stable states */ - STATE_ZOMBIE = 0, /* The threads handling the peer are not running for some reason */ + STATE_NEW = 0, /* The peer has been just been created, PSM thread not started yet */ STATE_OPEN, /* Connexion established */ /* Peer state machine */ @@ -167,10 +190,14 @@ /* Failover state machine */ STATE_SUSPECT, /* A DWR was sent and not answered within TwTime. Failover in progress. */ STATE_REOPEN, /* Connection has been re-established, waiting for 3 DWR/DWA exchanges before putting back to service */ + + /* Error state */ + STATE_ZOMBIE /* The PSM thread is not running anymore; it must be re-started or peer should be deleted. */ +#define STATE_MAX STATE_ZOMBIE }; extern const char *peer_state_str[]; #define STATE_STR(state) \ - (((unsigned)(state)) <= STATE_REOPEN ? peer_state_str[((unsigned)(state)) ] : "") + (((unsigned)(state)) <= STATE_MAX ? peer_state_str[((unsigned)(state)) ] : "") /* Information about a remote peer. Same structure is used for creating a new entry, but not all fields are meaningful in that case */ struct peer_info { @@ -220,23 +247,6 @@ struct fd_list pi_endpoints; /* Endpoint(s) of the remote peer (configured, discovered, or advertized). list of struct fd_endpoint. DNS resolved if empty. */ - /* TLS specific data -- the exact data pointed here depends on the security module in use (ex: gnutls, ...) */ - enum { - PI_SEC_GNUTLS = 0, /* The security module is GNUTLS, this is the default */ - PI_SEC_OTHER /* Another security module (TBD) */ - } pi_sec_module; - union { - /* Security data when pi_sec_module == PI_SEC_GNUTLS */ - struct { - void * CA; /* Authority to use to validate this peer credentials (a CA or root certificate) -- use default if NULL */ - void * cred; /* The (valid) credentials that the peer has presented */ - } gnutls; - /* Security data when pi_sec_module == PI_SEC_OTHER */ - struct { - void * dummy; /* Something meaningful for the other security module */ - } other; - } pi_sec_data; - /* The remaining information must not be modified, and is not used for peer creation */ enum peer_state pi_state; uint32_t pi_vendorid; /* Content of the Vendor-Id AVP, or 0 by default */ @@ -244,6 +254,11 @@ char * pi_prodname; /* copy of UTF-8 Product-Name AVP (\0 terminated) */ uint32_t pi_firmrev; /* Content of the Firmware-Revision AVP */ struct fd_list pi_apps; /* applications advertised by the remote peer, except relay (pi_flags.relay) */ + struct { + /* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo */ + const gnutls_datum_t *cert_list; /* The (valid) credentials that the peer has presented */ + unsigned int cert_list_size;/* Number of certificates in the list */ + } pi_sec_data; }; struct peer_hdr {