Mercurial > hg > freeDiameter
diff libfdcore/peers.c @ 706:4ffbc9f1e922
Large UNTESTED commit with the following changes:
* Improved DiameterIdentity handling (esp. interationalization issues),
and improve efficiency of some string operations in peers, sessions,
and dictionary modules (closes #7)
* Cleanup in the session module to free only unreferenced sessions (#16)
* Removed fd_cpu_flush_cache(), replaced by more robust alternatives.
* Improved peer state machine algorithm to counter SCTP multistream race
condition.
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Wed, 09 Feb 2011 15:26:58 +0900 |
parents | 78b665400097 |
children | 8b03c672261f |
line wrap: on
line diff
--- a/libfdcore/peers.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/peers.c Wed Feb 09 15:26:58 2011 +0900 @@ -72,6 +72,8 @@ fd_list_init(&p->p_hdr.info.runtime.pir_apps, p); p->p_eyec = EYEC_PEER; + CHECK_POSIX( pthread_mutex_init(&p->p_state_mtx, NULL) ); + fd_list_init(&p->p_actives, p); fd_list_init(&p->p_expiry, p); CHECK_FCT( fd_fifo_new(&p->p_tosend) ); @@ -93,22 +95,32 @@ 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); + if (info->config.pic_realm) { + if (!fd_os_is_valid_DiameterIdentity((os0_t)info->config.pic_realm, strlen(info->config.pic_realm))) { + TRACE_DEBUG(INFO, "'%s' is not a valid DiameterIdentity.", info->config.pic_realm); + return EINVAL; + } + } + /* 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) ); + p->p_hdr.info.pi_diamid = info->pi_diamid; + CHECK_FCT( fd_os_validate_DiameterIdentity(&p->p_hdr.info.pi_diamid, &p->p_hdr.info.pi_diamidlen, 1) ); memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) ); + /* Duplicate the strings if provided */ if (info->config.pic_realm) { CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) ); } if (info->config.pic_priority) { - CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_priority) ); + CHECK_MALLOC( p->p_hdr.info.config.pic_priority = strdup(info->config.pic_priority) ); } /* Move the list of endpoints into the peer */ @@ -123,7 +135,7 @@ if (orig_dbg) { CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) ); } else { - CHECK_MALLOC( p->p_dbgorig = strdup("unknown") ); + CHECK_MALLOC( p->p_dbgorig = strdup("unspecified") ); } p->p_cb = cb; p->p_cb_data = cb_data; @@ -133,7 +145,8 @@ for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { struct fd_peer * next = (struct fd_peer *)li; - int cmp = strcasecmp( p->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamid ); + int cmp = fd_os_almostcasecmp( p->p_hdr.info.pi_diamid, p->p_hdr.info.pi_diamidlen, + next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen ); if (cmp > 0) continue; if (cmp == 0) @@ -161,12 +174,11 @@ } /* Search for a peer */ -int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer ) +int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer ) { struct fd_list * li; - - TRACE_ENTRY("%p %p", diamid, peer); - CHECK_PARAMS( diamid && peer ); + TRACE_ENTRY("%p %zd %d %p", diamid, diamidlen, igncase, peer); + CHECK_PARAMS( diamid && diamidlen && peer ); *peer = NULL; @@ -174,7 +186,11 @@ CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { struct fd_peer * next = (struct fd_peer *)li; - int cmp = strcasecmp( diamid, next->p_hdr.info.pi_diamid ); + int cmp; + if (igncase) + cmp = fd_os_almostcasecmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen ); + else + cmp = fd_os_cmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen ); if (cmp > 0) continue; if (cmp == 0) @@ -254,6 +270,7 @@ fd_list_unlink(&p->p_actives); CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ ); + CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_state_mtx), /* continue */); CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */); CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */); @@ -282,10 +299,9 @@ 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; + struct fd_peer * peer = (struct fd_peer *)li->o; - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) { + if (fd_peer_getstate(peer) != STATE_ZOMBIE) { CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ ); } else { li = li->prev; /* to avoid breaking the loop */ @@ -306,14 +322,13 @@ while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) { /* Allow the PSM(s) to execute */ - sched_yield(); + usleep(100000); /* 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; - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) { + struct fd_peer * peer = (struct fd_peer *)li->o; + if (fd_peer_getstate(peer) == 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); @@ -328,7 +343,7 @@ 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); + struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next->o); fd_psm_abord(peer); fd_list_unlink(&peer->p_hdr.chain); fd_list_insert_before(&purge, &peer->p_hdr.chain); @@ -338,7 +353,7 @@ /* Free memory objects of all peers */ while (!FD_IS_LIST_EMPTY(&purge)) { - struct fd_peer * peer = (struct fd_peer *)(purge.next); + struct fd_peer * peer = (struct fd_peer *)(purge.next->o); fd_list_unlink(&peer->p_hdr.chain); fd_peer_free(&peer); } @@ -363,9 +378,9 @@ return; } - fd_log_debug("> %s\t%s", STATE_STR(peer->p_hdr.info.runtime.pir_state), peer->p_hdr.info.pi_diamid); + fd_log_debug("> %s\t%s", STATE_STR(fd_peer_getstate(peer)), peer->p_hdr.info.pi_diamid); if (details > INFO) { - fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "(unknown)"); + fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "<unknown>"); if (peer->p_hdr.info.runtime.pir_prodname) fd_log_debug("\t['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev); } @@ -397,19 +412,20 @@ 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; + struct fd_peer * np = (struct fd_peer *)li->o; fd_peer_dump(np, details); } CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ ); } +static struct dict_object *avp_oh_model = NULL; +static pthread_mutex_t cache_avp_lock = PTHREAD_MUTEX_INITIALIZER; + /* Handle an incoming CER request on a new connection */ int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx ) { struct msg * msg; - struct dict_object *avp_oh_model; - avp_code_t code = AC_ORIGIN_HOST; struct avp *avp_oh; struct avp_hdr * avp_hdr; struct fd_list * li; @@ -423,20 +439,40 @@ msg = *cer; + /* If needed, resolve the dictioanry model for Origin-Host */ + CHECK_POSIX( pthread_mutex_lock(&cache_avp_lock) ); + if (!avp_oh_model) { + avp_code_t code = AC_ORIGIN_HOST; + int ret; + CHECK_FCT_DO( ret = fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT), + { CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) ); return ret; } ); + } + CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) ); + /* Find the Diameter Identity of the remote peer in the message */ - CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT) ); CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) ); + ASSERT(avp_oh); /* otherwise it should not have passed rules validation, right? */ CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) ); + /* First, check if the Origin-Host value */ + if (!fd_os_is_valid_DiameterIdentity(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len)) { + TRACE_DEBUG(INFO, "Received new CER with invalid \\0 in its Origin-Host"); + CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ) ); + CHECK_FCT( fd_msg_rescode_set(*cer, "ER_DIAMETER_INVALID_AVP_VALUE", + "Your Origin-Host contains invalid characters.", avp_oh, 1 ) ); + CHECK_FCT( fd_out_send(cer, *cnx, NULL, FD_CNX_ORDERED) ); + return EINVAL; + } + /* Search if we already have this peer id in our list. We take directly the write lock so that we don't need to upgrade if it is a new peer. * There is space for a small optimization here if needed. */ CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - peer = (struct fd_peer *)li; - /* It is probably unwise to use strcasecmp on UTF8 data... To be improved! */ - int cmp = strncasecmp( (char *)avp_hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.len ); + int cmp; + peer = (struct fd_peer *)li->o; + cmp = fd_os_almostcasecmp( avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen ); if (cmp > 0) continue; if (cmp == 0) @@ -450,9 +486,9 @@ CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out ); /* Set the peer Diameter Id and the responder flag parameters */ - CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = malloc(avp_hdr->avp_value->os.len + 1), { ret = ENOMEM; goto out; } ); - memcpy(peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len); - peer->p_hdr.info.pi_diamid[avp_hdr->avp_value->os.len] = '\0'; + CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = os0dup(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len), + { ret = ENOMEM; goto out; } ); + peer->p_hdr.info.pi_diamidlen = avp_hdr->avp_value->os.len; CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } ); peer->p_flags.pf_responder = 1; peer->p_flags.pf_delete = 1; @@ -469,16 +505,18 @@ CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out ); } else { /* Check if the peer is in zombie state */ - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) { + if (fd_peer_getstate(peer) == STATE_ZOMBIE) { /* Re-activate the peer */ if (peer->p_hdr.info.config.pic_flags.exp) peer->p_flags.pf_responder = 1; - peer->p_hdr.info.runtime.pir_state = STATE_NEW; + CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), ); + peer->p_state = STATE_NEW; + CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), ); + peer->p_flags.pf_localterm = 0; CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out ); } } - + /* Send the new connection event to the PSM */ CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } ); memset(ev_data, 0, sizeof(ev_data));