# HG changeset patch # User Sebastien Decugis # Date 1298008237 -32400 # Node ID 571b3abaa5df4b9ef7b25d40a605707ec5b62953 # Parent 4784f2cf325f282b76d1796820275cfc51da8550 Support for Diameter Redirects through rt_redirect.fdx extension (EXPERIMENTAL) diff -r 4784f2cf325f -r 571b3abaa5df contrib/debian/changelog --- a/contrib/debian/changelog Thu Feb 17 16:37:15 2011 +0900 +++ b/contrib/debian/changelog Fri Feb 18 14:50:37 2011 +0900 @@ -19,8 +19,9 @@ * Removed fd_cpu_flush_cache(), replaced by more robust alternatives. * Improved peer state machine algorithm to counter SCTP multistream race condition. + * New extension rt_redirect.fdx that handles the Diameter Redirect errors. - -- Sebastien Decugis Mon, 07 Feb 2011 17:24:20 +0900 + -- Sebastien Decugis Fri, 18 Feb 2011 14:47:24 +0900 freediameter (1.0.4) UNRELEASED; urgency=low diff -r 4784f2cf325f -r 571b3abaa5df extensions/CMakeLists.txt --- a/extensions/CMakeLists.txt Thu Feb 17 16:37:15 2011 +0900 +++ b/extensions/CMakeLists.txt Fri Feb 18 14:50:37 2011 +0900 @@ -61,8 +61,9 @@ #### # Routing extensions -FD_EXTENSION_SUBDIR(rt_default "Configurable routing rules for freeDiameter" ON) -FD_EXTENSION_SUBDIR(rt_ereg "Configurable routing based on regexp matching of AVP values" OFF) +FD_EXTENSION_SUBDIR(rt_default "Configurable routing rules for freeDiameter" ON) +FD_EXTENSION_SUBDIR(rt_redirect "Handling of Diameter Redirect messages" ON) +FD_EXTENSION_SUBDIR(rt_ereg "Configurable routing based on regexp matching of AVP values" OFF) #### diff -r 4784f2cf325f -r 571b3abaa5df extensions/app_radgw/rgw_clients.c --- a/extensions/app_radgw/rgw_clients.c Thu Feb 17 16:37:15 2011 +0900 +++ b/extensions/app_radgw/rgw_clients.c Fri Feb 18 14:50:37 2011 +0900 @@ -214,7 +214,7 @@ { struct rgw_client *tmp = NULL; DiamId_t fqdn; - size_t fqdn_len; + size_t fqdn_len = 0; int ret, i; int loc = 0; @@ -549,9 +549,9 @@ int valid_nas_info = 0; struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL; size_t nas_id_len; - char * oh_str = NULL; size_t oh_strlen; int oh_free = 0; - char * or_str = NULL; size_t or_strlen; - char * rr_str = NULL; size_t rr_strlen; + char * oh_str = NULL; size_t oh_strlen = 0; int oh_free = 0; + char * or_str = NULL; size_t or_strlen = 0; + char * rr_str = NULL; size_t rr_strlen = 0; char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */ struct avp *avp = NULL; @@ -788,6 +788,7 @@ rr_strlen = cli->fqdn_len; } oh_str = &buf[0]; /* The canonname resolved */ + oh_strlen = 0; CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1), { TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret)); diff -r 4784f2cf325f -r 571b3abaa5df extensions/app_radgw/rgwx_sip.c --- a/extensions/app_radgw/rgwx_sip.c Thu Feb 17 16:37:15 2011 +0900 +++ b/extensions/app_radgw/rgwx_sip.c Fri Feb 18 14:50:37 2011 +0900 @@ -594,7 +594,7 @@ { //We extract Realm from Digest_URI DiamId_t realm=NULL; - size_t realm_len; + size_t realm_len = 0; os0_t temp; temp = (os0_t)(attr + 1); diff -r 4784f2cf325f -r 571b3abaa5df extensions/rt_redirect/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_redirect/CMakeLists.txt Fri Feb 18 14:50:37 2011 +0900 @@ -0,0 +1,22 @@ +# The rt_redirect extension +PROJECT("Diameter Redirect messages support" C) + +SET(RT_REDIR_SRC + rt_redir.h + rt_redir.c + redir_entries.c + redir_expiry.c + redir_fwd.c + redir_out.c + ) + +# Compile as a module +FD_ADD_EXTENSION(rt_redirect ${RT_REDIR_SRC}) + + +#### +## INSTALL section ## + +INSTALL(TARGETS rt_redirect + LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX} + COMPONENT freeDiameter-daemon) diff -r 4784f2cf325f -r 571b3abaa5df extensions/rt_redirect/redir_entries.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_redirect/redir_entries.c Fri Feb 18 14:50:37 2011 +0900 @@ -0,0 +1,319 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2011, 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 "rt_redir.h" + +/* The array with all entries ordered by their data */ +struct redir_line redirects_usages[H_U_MAX + 1]; + +/* Initialize the array */ +int redir_entry_init() +{ + int i; + + TRACE_ENTRY(""); + + /* redirects_usages */ + memset(&redirects_usages, 0, sizeof(redirects_usages)); + + for (i = 0; i <= H_U_MAX; i++) { + CHECK_POSIX( pthread_rwlock_init( &redirects_usages[i].lock, NULL) ); + fd_list_init( &redirects_usages[i].sentinel, &redirects_usages[i] ); + } + + /* initialize the scores */ + redirects_usages[ DONT_CACHE ].score = FD_SCORE_REDIR_ONCE; + redirects_usages[ ALL_SESSION ].score = FD_SCORE_REDIR_SESSION; + redirects_usages[ ALL_REALM ].score = FD_SCORE_REDIR_REALM; + redirects_usages[ REALM_AND_APPLICATION ].score = FD_SCORE_REDIR_REALM_APP; + redirects_usages[ ALL_APPLICATION ].score = FD_SCORE_REDIR_APP; + redirects_usages[ ALL_HOST ].score = FD_SCORE_REDIR_HOST; + redirects_usages[ ALL_USER ].score = FD_SCORE_REDIR_USER; + + return 0; +} + + +/* Create a new redir_entry and add the correct data */ +int redir_entry_new(struct redir_entry ** e, struct fd_list * targets, uint32_t rhu, struct msg * qry, DiamId_t nh, size_t nhlen, os0_t oh, size_t ohlen) +{ + struct redir_entry * entry = NULL; + os0_t s; + size_t l; + + TRACE_ENTRY("%p %p %d %p %p %zd %p %zd", e, targets, rhu, qry, nh, nhlen, oh, ohlen) + ASSERT(e && targets && (rhu <= H_U_MAX) && qry && nh && nhlen && oh && ohlen); + + CHECK_MALLOC( entry = malloc(sizeof(struct redir_entry)) ); + memset(entry, 0, sizeof(struct redir_entry)); + + entry->eyec = REDIR_ENTRY_EYEC; + + CHECK_MALLOC( entry->from.s = os0dup(nh, nhlen) ); + entry->from.l = nhlen; + + fd_list_init(&entry->target_peers_list, entry); + fd_list_move_end(&entry->target_peers_list, targets); + + fd_list_init(&entry->exp_list, entry); + + entry->type = rhu; + fd_list_init(&entry->redir_list, entry); + /* finally initialize the data */ + switch (rhu) { + case DONT_CACHE: + entry->data.message.msg = qry; + break; + + case ALL_SESSION: + { + /* There is a good chance that the session is already cached in the message, so retrieve it */ + struct session * sess; + CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, qry, &sess, NULL) ); + if (!sess) { + TRACE_DEBUG(INFO, "Received a Redirect indication with usage ALL_SESSION but no Session-Id AVP in the message, defaulting to DONT_CACHE"); + entry->type = DONT_CACHE; + entry->data.message.msg = qry; + break; + } + CHECK_FCT( fd_sess_getsid(sess, &s, &l) ); + CHECK_MALLOC( entry->data.session.s = os0dup(s, l) ); + entry->data.session.l = l; + } + break; + + case ALL_REALM: + { + /* Search the Destination-Realm of the message */ + struct avp * dr; + struct avp_hdr * ahdr; + CHECK_FCT( fd_msg_search_avp(qry, redir_dict_dr, &dr) ); + if (!dr) { + TRACE_DEBUG(INFO, "Received a Redirect indication with usage ALL_REALM but no Destination-Realm AVP in the message, defaulting to DONT_CACHE"); + entry->type = DONT_CACHE; + entry->data.message.msg = qry; + break; + } + CHECK_FCT( fd_msg_avp_hdr( dr, &ahdr ) ); + CHECK_MALLOC( entry->data.realm.s = os0dup(ahdr->avp_value->os.data, ahdr->avp_value->os.len) ); + entry->data.realm.l = ahdr->avp_value->os.len; + } + break; + + case REALM_AND_APPLICATION: + { + /* Search the Destination-Realm of the message */ + struct avp * dr; + struct avp_hdr * ahdr; + CHECK_FCT( fd_msg_search_avp(qry, redir_dict_dr, &dr) ); + if (!dr) { + TRACE_DEBUG(INFO, "Received a Redirect indication with usage REALM_AND_APPLICATION but no Destination-Realm AVP in the message, defaulting to DONT_CACHE"); + entry->type = DONT_CACHE; + entry->data.message.msg = qry; + break; + } + CHECK_FCT( fd_msg_avp_hdr( dr, &ahdr ) ); + CHECK_MALLOC( entry->data.realm_app.s = os0dup(ahdr->avp_value->os.data, ahdr->avp_value->os.len) ); + entry->data.realm_app.l = ahdr->avp_value->os.len; + } + /* and then the application */ + { + struct msg_hdr * hdr; + CHECK_FCT( fd_msg_hdr(qry, &hdr) ); + entry->data.realm_app.a = hdr->msg_appl; + } + break; + + case ALL_APPLICATION: + { + struct msg_hdr * hdr; + CHECK_FCT( fd_msg_hdr(qry, &hdr) ); + entry->data.app.a = hdr->msg_appl; + } + break; + + case ALL_HOST: + CHECK_MALLOC( entry->data.host.s = os0dup(oh, ohlen) ); + entry->data.host.l = ohlen; + break; + + case ALL_USER: + { + /* Search the User-Name of the message */ + struct avp * un; + struct avp_hdr * ahdr; + CHECK_FCT( fd_msg_search_avp(qry, redir_dict_un, &un) ); + if (!un) { + TRACE_DEBUG(INFO, "Received a Redirect indication with usage ALL_USER but no User-Name AVP in the message, defaulting to DONT_CACHE"); + entry->type = DONT_CACHE; + entry->data.message.msg = qry; + break; + } + CHECK_FCT( fd_msg_avp_hdr( un, &ahdr ) ); + CHECK_MALLOC( entry->data.user.s = os0dup(ahdr->avp_value->os.data, ahdr->avp_value->os.len) ); + entry->data.user.l = ahdr->avp_value->os.len; + } + break; + + default: + ASSERT(0); + return EINVAL; + } + + /* We're done */ + *e = entry; + return 0; +} + + +/* Compares two pointers (DONT_CACHE) */ +static int compare_entries_ptr(union matchdata * d1, union matchdata * d2) { + unsigned long v1 = (unsigned long) d1->message.msg; + unsigned long v2 = (unsigned long) d2->message.msg; + if (v1 > v2) + return 1; + if (v1 < v2) + return -1; + return 0; +} +/* Compare two applications (REALM_AND_APPLICATION and ALL_APPLICATION) */ +static int compare_entries_appl(union matchdata * d1, union matchdata * d2) { + if (d1->app.a > d2->app.a) + return 1; + if (d1->app.a < d2->app.a) + return -1; + return 0; +} + +/* Compare two strings (ALL_SESSION, ALL_REALM, ALL_HOST, ALL_USER) */ +static int compare_entries_ostr(union matchdata * d1, union matchdata * d2) { + return fd_os_cmp(d1->session.s, d1->session.l, d2->session.s, d2->session.l); +} + +/* The array of callbacks */ +int (*redir_entry_cmp_key[H_U_MAX +1])(union matchdata * , union matchdata * ) = { + compare_entries_ptr, /* DONT_CACHE */ + compare_entries_ostr, /* ALL_SESSION */ + compare_entries_ostr, /* ALL_REALM */ + compare_entries_appl, /* REALM_AND_APPLICATION */ + compare_entries_appl, /* ALL_APPLICATION */ + compare_entries_ostr, /* ALL_HOST */ + compare_entries_ostr /* ALL_USER */ +}; + +/* Link the newly created entry into the correct redirects_usages list. The mutex must be held */ +int redir_entry_insert(struct redir_entry * e) +{ + struct fd_list * li; + + TRACE_ENTRY("%p", e); + CHECK_PARAMS(e && (e->eyec == REDIR_ENTRY_EYEC)); + + /* Write-Lock the line */ + CHECK_POSIX( pthread_rwlock_wrlock( RWLOCK_REDIR(e) ) ); + + for (li = redirects_usages[e->type].sentinel.next; li != &redirects_usages[e->type].sentinel; li = li->next) { + struct redir_entry * n = li->o; + int cmp = redir_entry_cmp_key[e->type](&e->data, &n->data); + if (cmp <= 0) + break; + } + + fd_list_insert_before(li, &e->redir_list); + + /* unLock the line */ + CHECK_POSIX( pthread_rwlock_unlock( RWLOCK_REDIR(e) ) ); + + return 0; +} + +/* Destroy -- the exp_peer_lock must be held when this function is called */ +int redir_entry_destroy(struct redir_entry * e) +{ + TRACE_ENTRY("%p", e); + CHECK_PARAMS(e && (e->eyec == REDIR_ENTRY_EYEC)); + + /* If the entry is linked, lock the rwlock also */ + if (!FD_IS_LIST_EMPTY(&e->redir_list)) { + CHECK_POSIX( pthread_rwlock_wrlock( RWLOCK_REDIR(e) ) ); + fd_list_unlink(&e->redir_list); + CHECK_POSIX( pthread_rwlock_unlock( RWLOCK_REDIR(e) ) ); + } + + /* Now unlink from other list */ + fd_list_unlink(&e->exp_list); + + /* Empty the targets list */ + while (!FD_IS_LIST_EMPTY(&e->target_peers_list)) { + struct redir_host * h = (struct redir_host *)e->target_peers_list.next->o; + + fd_list_unlink(&h->chain); + free(h->id); + free(h); + } + + /* Now we can destroy the data safely */ + switch (e->type) { + case DONT_CACHE: + /* nothing special */ + break; + case ALL_SESSION: + free(e->data.session.s); + break; + case ALL_REALM: + free(e->data.realm.s); + break; + case REALM_AND_APPLICATION: + free(e->data.realm_app.s); + break; + case ALL_APPLICATION: + break; + case ALL_HOST: + free(e->data.host.s); + break; + case ALL_USER: + free(e->data.user.s); + break; + default: + TRACE_DEBUG(INFO, "Invalid redirect type was saved"); + ASSERT(0); + return EINVAL; + } + + free(e->from.s); + + free(e); + return 0; +} diff -r 4784f2cf325f -r 571b3abaa5df extensions/rt_redirect/redir_expiry.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_redirect/redir_expiry.c Fri Feb 18 14:50:37 2011 +0900 @@ -0,0 +1,136 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2011, 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 "rt_redir.h" + +/* Expiration management */ + + +/* Entries by their ascending expiration date, to accelerate the work of the expire thread */ +static struct fd_list expire_list = FD_LIST_INITIALIZER(expire_list); +static pthread_cond_t exp_cnd = PTHREAD_COND_INITIALIZER; + +pthread_mutex_t redir_exp_peer_lock = PTHREAD_MUTEX_INITIALIZER; + +/* The thread that handles expired entries cleanup. */ +void * redir_exp_thr_fct(void * arg) +{ + fd_log_threadname ( "Redirects/expire" ); + TRACE_ENTRY( "" ); + + CHECK_POSIX_DO( pthread_mutex_lock(&redir_exp_peer_lock), goto fatal_error ); + pthread_cleanup_push( fd_cleanup_mutex, &redir_exp_peer_lock ); + + do { + struct timespec now; + struct redir_entry * first; +again: + /* Check if there are expiring entries available */ + if (FD_IS_LIST_EMPTY(&expire_list)) { + /* Just wait for a change or cancelation */ + CHECK_POSIX_DO( pthread_cond_wait( &exp_cnd, &redir_exp_peer_lock ), break /* this might not pop the cleanup handler, but since we ASSERT(0), it is not the big issue... */ ); + /* Restart the loop on wakeup */ + goto again; + } + + /* Get the pointer to the entry that expires first */ + first = (struct redir_entry *)(expire_list.next->o); + + /* Get the current time */ + CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), break ); + + /* If first session is not expired, we just wait until it happens */ + if ( TS_IS_INFERIOR( &now, &first->timeout ) ) { + + CHECK_POSIX_DO2( pthread_cond_timedwait( &exp_cnd, &redir_exp_peer_lock, &first->timeout ), + ETIMEDOUT, /* ETIMEDOUT is a normal error, continue */, + /* on other error, */ break ); + + /* on wakeup, loop */ + goto again; + } + + /* Now, the first entry in the list is expired; destroy it */ + + CHECK_FCT_DO( redir_entry_destroy( first ), break ); + + } while (1); + + pthread_cleanup_pop( 0 ); + CHECK_POSIX_DO( pthread_mutex_unlock(&redir_exp_peer_lock), ); + +fatal_error: + TRACE_DEBUG(INFO, "A system error occurred in redirect module! Expiry thread is terminating..."); + ASSERT(0); + return NULL; +} + +/* Sets the timeout value & link in expiry list. The mutex must be held on calling */ +int redir_exp_set(struct redir_entry * e, uint32_t duration) +{ + struct fd_list * li; + TRACE_ENTRY("%p %d", e, duration); + CHECK_PARAMS(e && (e->eyec == REDIR_ENTRY_EYEC) && duration ); + + /* Unlink in case it was already set before */ + fd_list_unlink(&e->exp_list); + + /* Get current time */ + CHECK_SYS( clock_gettime(CLOCK_REALTIME, &e->timeout) ); + + /* Add the duration */ + e->timeout.tv_sec += duration; + + /* now search the next element in the list */ + for (li = expire_list.next; li != &expire_list; li = li->next) { + struct redir_entry * n = li->o; + + if ( TS_IS_INFERIOR( &e->timeout, &n->timeout ) ) + break; + + } + + /* Insert before this element */ + fd_list_insert_before(li, &e->exp_list); + + /* Signal the expiry thread if needed */ + if (e->exp_list.prev == &expire_list) { /* it is the first element */ + CHECK_POSIX( pthread_cond_signal(&exp_cnd) ); + } + + /* Done */ + return 0; +} + diff -r 4784f2cf325f -r 571b3abaa5df extensions/rt_redirect/redir_fwd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_redirect/redir_fwd.c Fri Feb 18 14:50:37 2011 +0900 @@ -0,0 +1,272 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2011, 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 "rt_redir.h" + +/* This structure contains the data to keep when a new peer's connection is attempted (for later) */ +struct redir_task { + struct msg * answer; /* the message that was being processed */ + uint32_t rhu; /* Redirect-Host-Usage value */ + uint32_t rmct; /* Redirect-Max-Cache-Time value */ + struct fd_list rh; /* the list of Redirect-Hosts */ +}; + +/* Received answers FWD callback */ +int redir_fwd_cb(void * cbdata, struct msg ** msg) +{ + struct msg * m, * q; + struct rt_data *rtd; + struct msg_hdr * hdr; + union avp_value *a_rc = NULL, *a_rhu = NULL, *a_rmct = NULL, *a_oh = NULL; + int known = 0, actives = 0; + struct fd_list * li; + struct avp * avp; + struct redir_task task = { .answer = NULL, .rhu = 0, .rmct = 0, .rh = FD_LIST_INITIALIZER(task.rh) }; + DiamId_t nh; + size_t nhlen; + int nbrh = 0; + struct redir_entry * entry; + + TRACE_ENTRY("%p %p", cbdata, msg); + + CHECK_PARAMS(msg && *msg); + + m = *msg; + + /* First get the header */ + CHECK_FCT( fd_msg_hdr(m, &hdr) ); + + /* Check if we have an error */ + ASSERT(!(hdr->msg_flags & CMD_FLAG_REQUEST)); + if (!(hdr->msg_flags & CMD_FLAG_ERROR)) { + /* This answer does not have the E flag, no need to process further */ + return 0; + } + + /* Now get the AVPs we are interested in */ + CHECK_FCT( fd_msg_browse(m, MSG_BRW_FIRST_CHILD, &avp, NULL) ); + while (avp) { + struct avp_hdr * ahdr; + + CHECK_FCT( fd_msg_avp_hdr( avp, &ahdr ) ); + if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) { + switch (ahdr->avp_code) { + case AC_ORIGIN_HOST: + /* Parse this AVP */ + CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); + ASSERT( ahdr->avp_value ); + a_oh = ahdr->avp_value; + break; + + case AC_RESULT_CODE: + /* Parse this AVP */ + CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); + ASSERT( ahdr->avp_value ); + a_rc = ahdr->avp_value; + + if (a_rc->u32 != ER_DIAMETER_REDIRECT_INDICATION) { + /* It is not a REDIRECT error, we don't do anything */ + goto out; + } + break; + + case AC_REDIRECT_HOST: + { + struct redir_host * h = NULL; + DiamId_t id = NULL; + size_t len = 0; + int secure = 0; + uint16_t port = 0; + int l4 = 0; + char proto = 0; + + /* Parse this AVP */ + CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); + ASSERT( ahdr->avp_value ); + + nbrh++; + + CHECK_FCT_DO( fd_os_parse_DiameterURI(ahdr->avp_value->os.data, ahdr->avp_value->os.len, + &id, &len, &secure, &port, &l4, &proto), + { + TRACE_DEBUG(INFO, "Received an invalid Redirect-Host AVP value ('%.*s'), ignored", ahdr->avp_value->os.len, ahdr->avp_value->os.data); + break; + } ); + + /* Now check if the transport & protocol are supported */ + if (proto && (proto != 'd')) { + TRACE_DEBUG(FULL, "Ignored unsupported non-Diameter Redirect-Host AVP (%.*s)", ahdr->avp_value->os.len, ahdr->avp_value->os.data); + free(id); + break; + } + if (l4 && (l4 == IPPROTO_UDP)) { + TRACE_DEBUG(FULL, "Ignored unsupported UDP Redirect-Host AVP (%.*s)", ahdr->avp_value->os.len, ahdr->avp_value->os.data); + free(id); + break; + } + + /* It looks OK, save this entry. */ + + CHECK_MALLOC( h = malloc(sizeof(struct redir_host)) ); + memset(h, 0, sizeof(struct redir_host)); + fd_list_init(&h->chain, h); + h->id = id; + h->len = len; + /* later: secure, port */ + + /* The list is kept ordered by id so that it is faster to compare to candidates later */ + for (li = task.rh.next; li != &task.rh; li = li->next) { + struct redir_host * nhost = li->o; + if ( fd_os_almostcasecmp(id, len, nhost->id, nhost->len) <= 0 ) + break; + } + fd_list_insert_before(li, &h->chain); + } + break; + + case AC_REDIRECT_HOST_USAGE: + /* Parse this AVP */ + CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); + ASSERT( ahdr->avp_value ); + a_rhu = ahdr->avp_value; + if (a_rhu->u32 > H_U_MAX) { + TRACE_DEBUG(INFO, "Received unsupported Redirect-Host-Usage value (%d), defaulting to DONT_CACHE", a_rhu->u32); + } else { + task.rhu = a_rhu->u32; + } + break; + + case AC_REDIRECT_MAX_CACHE_TIME: + /* Parse this AVP */ + CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); + ASSERT( ahdr->avp_value ); + a_rmct = ahdr->avp_value; + task.rmct = a_rmct->u32; + break; + + } + } + + /* Go to next AVP */ + CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); + } + + /* Check we have received the necessary information */ + if (!a_rc) { + TRACE_DEBUG(FULL, "Invalid Diameter answer without a Result-Code AVP, Redirect module gave up"); + goto out; + } + + if (!a_oh) { + TRACE_DEBUG(FULL, "Invalid Diameter answer without an Origin-Host AVP, Redirect module gave up"); + goto out; + } + + if (FD_IS_LIST_EMPTY(&task.rh)) { + TRACE_DEBUG(FULL, "Diameter answer with a DIAMETER_REDIRECT_INDICATION Result-Code AVP but no valid/supported Redirect-Host AVP, Redirect module gave up"); + goto out; + } + + if (a_rhu && (task.rhu != DONT_CACHE) && !a_rmct) { + TRACE_DEBUG(FULL, "Invalid Diameter Redirect answer with a Redirect-Host-Usage AVP but no Redirect-Max-Cache-Time, Redirect module gave up"); + goto out; + } + + /* It looks like we can process the Redirect indication */ + + /* Search for the peers we already know */ + for (li = task.rh.next; li != &task.rh; li = li->next) { + struct redir_host * h = li->o; + struct peer_hdr * peer; + + CHECK_FCT( fd_peer_getbyid( h->id, h->len, 1, &peer ) ); + if (peer) { + known ++; + memcpy(h->id, peer->info.pi_diamid, h->len); /* Overwrite the case so we can search case-sensitive from here on */ + if (fd_peer_get_state(peer) == STATE_OPEN) { + actives ++; + } + } + } + + TRACE_DEBUG(FULL, "Redirect module: received %d Redirect-Hosts, %d are known peers, %d have an OPEN connection", nbrh, known, actives); + + /* in this version, we only redirect when there are known active peers. TODO: add new peers via fd_peer_add when no active peer is available */ + + if (!actives) { + TRACE_DEBUG(INFO, "Unable to comply to Redirect indication: none of the peers included is in OPEN state"); + goto out; + } + + /* From this point, we will re-send the query to a different peer, so stop forwarding the answer here */ + *msg = NULL; + + /* Get the query's routing data & add the new error */ + CHECK_FCT( fd_msg_answ_getq(m, &q) ); + CHECK_FCT( fd_msg_rt_get(q, &rtd) ); + CHECK_FCT( fd_msg_source_get( m, &nh, &nhlen ) ); + CHECK_FCT( fd_rtd_error_add(rtd, nh, nhlen, a_oh->os.data, a_oh->os.len, a_rc->u32) ); + + /* Create a redir_rule */ + CHECK_FCT( redir_entry_new(&entry, &task.rh, task.rhu, q, nh, nhlen, a_oh->os.data, a_oh->os.len) ); + + CHECK_POSIX( pthread_mutex_lock(&redir_exp_peer_lock) ); + /* Insert in the split list */ + CHECK_FCT( redir_entry_insert(entry) ); + /* Set the expiry */ + CHECK_FCT( redir_exp_set(entry, task.rmct ?: DEFAULT_EXPIRE_TIME) ); + CHECK_POSIX( pthread_mutex_unlock(&redir_exp_peer_lock) ); + + /* Now we can get rid of the received answer and send again the query. */ + CHECK_FCT( fd_msg_answ_detach(m) ); + CHECK_FCT( fd_msg_free(m) ); + + /* Send it */ + CHECK_FCT( fd_msg_send(&q, NULL, NULL) ); + + /* Done! */ + +out: + while (!FD_IS_LIST_EMPTY(&task.rh)) { + struct redir_host * h = task.rh.next->o; + fd_list_unlink(&h->chain); + free(h->id); + free(h); + } + + return 0; + +} + diff -r 4784f2cf325f -r 571b3abaa5df extensions/rt_redirect/redir_out.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_redirect/redir_out.c Fri Feb 18 14:50:37 2011 +0900 @@ -0,0 +1,280 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2011, 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 "rt_redir.h" + + +/* Find the data pertinent to a type in the input data */ +static int get_data_to_match(enum redir_h_u type, struct msg * msg, union matchdata * data, int * nodata) +{ + TRACE_ENTRY("%d %p %p %p", type, msg, data, nodata); + + /* Initialize the data area */ + memset(data, 0, sizeof(union matchdata)); + *nodata = 0; + + /* Now, find the appropriate information, depending on type */ + switch (type) { + case DONT_CACHE: + data->message.msg = msg; + break; + + case ALL_SESSION: + { + /* Get the sid from the message */ + struct session * sess; + CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) ); + if (!sess) { + TRACE_DEBUG(ANNOYING, "Message %p cannot match any ALL_SESSION rule since it does not have a Session-Id", msg); + *nodata = 1; + } else { + CHECK_FCT( fd_sess_getsid(sess, &data->session.s, &data->session.l) ); + } + } + break; + + case ALL_REALM: + { + /* Search the Destination-Realm in the message */ + struct avp * dr; + CHECK_FCT( fd_msg_search_avp(msg, redir_dict_dr, &dr) ); + if (!dr) { + TRACE_DEBUG(ANNOYING, "Message %p cannot match any ALL_REALM rule since it does not have a Destination-Realm", msg); + *nodata = 1; + } else { + struct avp_hdr * ahdr; + CHECK_FCT( fd_msg_avp_hdr( dr, &ahdr ) ); + data->realm.s = ahdr->avp_value->os.data; + data->realm.l = ahdr->avp_value->os.len; + } + } + break; + + case REALM_AND_APPLICATION: + { + /* Search the Destination-Realm of the message */ + struct avp * dr; + CHECK_FCT( fd_msg_search_avp(msg, redir_dict_dr, &dr) ); + if (!dr) { + TRACE_DEBUG(ANNOYING, "Message %p cannot match any REALM_AND_APPLICATION rule since it does not have a Destination-Realm", msg); + *nodata = 1; + } else { + struct avp_hdr * ahdr; + CHECK_FCT( fd_msg_avp_hdr( dr, &ahdr ) ); + data->realm_app.s = ahdr->avp_value->os.data; + data->realm_app.l = ahdr->avp_value->os.len; + + /* and then the application */ + { + struct msg_hdr * hdr; + CHECK_FCT( fd_msg_hdr(msg, &hdr) ); + data->realm_app.a = hdr->msg_appl; + /* Should we forbid application 0? */ + } + } + } + break; + + case ALL_APPLICATION: + { + /* Retrieve the application from the message */ + struct msg_hdr * hdr; + CHECK_FCT( fd_msg_hdr(msg, &hdr) ); + data->app.a = hdr->msg_appl; + } + break; + + case ALL_HOST: + /* This is more complex, we need to match with all candidates in each rule, it'll be done later */ + break; + + case ALL_USER: + { + /* Search the User-Name of the message */ + struct avp * un; + CHECK_FCT( fd_msg_search_avp(msg, redir_dict_un, &un) ); + if (!un) { + TRACE_DEBUG(ANNOYING, "Message %p cannot match any ALL_USER rule since it does not have a User-Name", msg); + *nodata = 1; + } else { + struct avp_hdr * ahdr; + CHECK_FCT( fd_msg_avp_hdr( un, &ahdr ) ); + data->user.s = ahdr->avp_value->os.data; + data->user.l = ahdr->avp_value->os.len; + } + } + break; + + default: + ASSERT(0); + return EINVAL; + } + + return 0; +} + + +/* Apply the score from a rule if the candidate list is appropriate */ +static int apply_rule(struct redir_entry * e, struct msg * msg, struct fd_list * candidates) +{ + struct fd_list * lic, *lirh; + struct rtd_candidate * c_oh = NULL; + int cmp; + + TRACE_ENTRY("%p %p %p", e, msg, candidates); + ASSERT( e && msg && candidates ); + + if (FD_IS_LIST_EMPTY(candidates)) { + TRACE_DEBUG(ANNOYING, "Skip Redirect rule since candidates list is empty"); + return 0; + } + + /* Now search common peers between e->target_peers_list and candidates */ + TRACE_DEBUG(ANNOYING, "Message %p matches a Redirect rule (t:%d, @%p), processing candidates list", msg, e->type, e); + + /* First, decrease the score of the host that we received the previous Redirect from, in case it is in the list */ + for (lic = candidates->next; lic != candidates; lic = lic->next) { + struct rtd_candidate * cand = (struct rtd_candidate *) lic; + + /* Special case: ALL_HOST rules: we decrease the score of the Origin-Host if present */ + if (e->type == ALL_HOST) { + cmp = fd_os_almostcasecmp(cand->diamid, cand->diamidlen, e->data.host.s, e->data.host.l); + if (!cmp) { + TRACE_DEBUG(FULL, "Redirect msg %p: peer '%.*s' += %d (previous ALL_HOST Redirect originated from this peer)", msg, cand->diamidlen, cand->diamid, FD_SCORE_SENT_REDIRECT); + cand->score += FD_SCORE_SENT_REDIRECT; + c_oh = cand; + continue; + } + } + + cmp = fd_os_cmp(cand->diamid, cand->diamidlen, e->from.s, e->from.l); + if (!cmp) { + TRACE_DEBUG(FULL, "Redirect msg %p: peer '%.*s' += %d (previous Redirect received from this peer)", msg, cand->diamidlen, cand->diamid, FD_SCORE_SENT_REDIRECT); + cand->score += FD_SCORE_SENT_REDIRECT; + } + + } + + if ((e->type == ALL_HOST) && (c_oh == NULL)) { + /* The rule does not apply, we're done */ + return 0; + } + + /* for each candidate, if it is found in the target_peers list, we add the rule's score to this candidate */ + for (lic = candidates->next; lic != candidates; lic = lic->next) { + /* the candidates list is not guaranteed to be ordered at this time, so we cannot avoid the two imbricated loops */ + struct rtd_candidate * cand = (struct rtd_candidate *) lic; + + /* Is this candidate in the "Redirect-Host" list ? */ + for (lirh = e->target_peers_list.next; lirh != &e->target_peers_list; lirh = lirh->next) { + struct redir_host * host = lirh->o; + + cmp = fd_os_cmp( cand->diamid, cand->diamidlen, host->id, host->len ); + + if (cmp < 0) + break; /* targets are ordered */ + + if (cmp == 0) { + TRACE_DEBUG(FULL, "Redirect msg %p: peer '%.*s' += %d (rule t:%d @%p)", msg, cand->diamidlen, cand->diamid, redirects_usages[e->type].score, e->type, e); + cand->score += redirects_usages[e->type].score; + } + } + } + + return 0; +} + + +/* OUT callback */ +int redir_out_cb(void * cbdata, struct msg * msg, struct fd_list * candidates) +{ + int i, ret = 0; + + TRACE_ENTRY("%p %p %p", cbdata, msg, candidates); + + for (i = 0; i <= H_U_MAX; i++) { + /* Lock the line. We write lock in case of DONT_CACHE so we can directly unlink the entry. read in other cases is sufficient */ + if (i == DONT_CACHE) { + CHECK_POSIX( pthread_rwlock_wrlock( &redirects_usages[i].lock ) ); + } else { + CHECK_POSIX( pthread_rwlock_rdlock( &redirects_usages[i].lock ) ); + } + + if (!FD_IS_LIST_EMPTY(&redirects_usages[i].sentinel)) { + union matchdata data; + int nodata; /* The message does not allow to apply this rule, skip */ + + /* Retrieve the data that may match in the message */ + CHECK_FCT_DO( ret = get_data_to_match(i, msg, &data, &nodata), goto out ); + + /* If this message may match some of our rules */ + if (!nodata) { + struct fd_list * li; + /* Attempt each rule we have stored */ + for (li = redirects_usages[i].sentinel.next; li != &redirects_usages[i].sentinel; li = li->next) { + struct redir_entry * e = li->o; + + /* Does it match ? */ + if (i != ALL_HOST) { /* this one is an exception, we handle it separately */ + int cmp = redir_entry_cmp_key[i](&data, &e->data); + if (cmp > 0) + continue; + if (cmp < 0) + break; + } + + /* This rule matches (or we are in ALL_HOST), apply */ + CHECK_FCT_DO( ret = apply_rule(e, msg, candidates), goto out ); + + /* If this was a DONT_CACHE rule, we unlink it, so that it will not be used again */ + if (i == DONT_CACHE) { + li=li->prev; + fd_list_unlink( li->next ); + /* We cannot delete here without taking the mutex, which would mean we have first to release the lock... + just let expiry garbage collet the rule */ + } + } + } + + } +out: + CHECK_POSIX( pthread_rwlock_unlock( &redirects_usages[i].lock ) ); + if (ret) + return ret; + } + + return 0; +} + diff -r 4784f2cf325f -r 571b3abaa5df extensions/rt_redirect/rt_redir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_redirect/rt_redir.c Fri Feb 18 14:50:37 2011 +0900 @@ -0,0 +1,102 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2011, 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 "rt_redir.h" + +struct dict_object * redir_dict_dr = NULL; +struct dict_object * redir_dict_un = NULL; + +static struct fd_rt_fwd_hdl * fwd_hdl = NULL; +static struct fd_rt_out_hdl * out_hdl = NULL; +static pthread_t exp_thr = (pthread_t)NULL; + +/* Initialize the module */ +static int redir_entry(char * conffile) +{ + TRACE_ENTRY(""); + + /* Dictionary objects */ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &redir_dict_dr, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &redir_dict_un, ENOENT) ); + + /* Initialize the entries array */ + CHECK_FCT( redir_entry_init() ); + + /* Start the expire thread */ + CHECK_POSIX( pthread_create( &exp_thr, NULL, redir_exp_thr_fct, NULL ) ); + + /* Register the callback that receives the answers and processes when it contains a Redirect indication. */ + CHECK_FCT( fd_rt_fwd_register ( redir_fwd_cb, NULL, RT_FWD_ANS, &fwd_hdl ) ); + + /* Register the callback that applies the saved Redirect rules to outgoing messages. */ + CHECK_FCT( fd_rt_out_register ( redir_out_cb, NULL, 10, &out_hdl ) ); + + return 0; +} + +EXTENSION_ENTRY("rt_redirect", redir_entry); + +/* And terminate it */ +void fd_ext_fini(void) +{ + int i; + + /* Unregister the callbacks */ + if (fwd_hdl) { + CHECK_FCT_DO( fd_rt_fwd_unregister(fwd_hdl, NULL), ); + } + if (out_hdl) { + CHECK_FCT_DO( fd_rt_out_unregister(out_hdl, NULL), ); + } + + /* Stop the expiry thread */ + CHECK_FCT_DO( fd_thr_term(&exp_thr), ); + + /* Empty all entries */ + CHECK_POSIX_DO( pthread_mutex_lock(&redir_exp_peer_lock), ); + for (i = 0; i <= H_U_MAX; i++) { + CHECK_POSIX_DO( pthread_rwlock_wrlock( &redirects_usages[i].lock), ); + while (!FD_IS_LIST_EMPTY(&redirects_usages[i].sentinel)) { + struct redir_entry * e = redirects_usages[i].sentinel.next->o; + fd_list_unlink(&e->redir_list); + CHECK_FCT_DO( redir_entry_destroy(e), ); + } + CHECK_POSIX_DO( pthread_rwlock_unlock( &redirects_usages[i].lock), ); + CHECK_POSIX_DO( pthread_rwlock_destroy( &redirects_usages[i].lock), ); + } + CHECK_POSIX_DO( pthread_mutex_unlock(&redir_exp_peer_lock), ); + + return; +} diff -r 4784f2cf325f -r 571b3abaa5df extensions/rt_redirect/rt_redir.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_redirect/rt_redir.h Fri Feb 18 14:50:37 2011 +0900 @@ -0,0 +1,173 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2011, 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. * +*********************************************************************************************************/ + +/* Diameter Redirect management */ +#include + +/* There are 2 locks in this module. The priority is established as follow to avoid deadlocks: +exp_peer mutex > usages rwlock. +(e.g., the rwlock can be taken while holding the mutex, but not the other way) +*/ + +/* The types of redirects (from Redirect-Host-Usage AVP value) */ +enum redir_h_u { + DONT_CACHE = 0, + ALL_SESSION, + ALL_REALM, + REALM_AND_APPLICATION, + ALL_APPLICATION, + ALL_HOST, + ALL_USER +}; +#define H_U_MAX ALL_USER + +/* Eye catcher */ +#define REDIR_ENTRY_EYEC 0x43D14E74 + +/* Expiration time set for DONT_CACHE tasks, so that the entry is found when the code is called back */ +#define DEFAULT_EXPIRE_TIME 10 /* seconds */ + +/* Structure to store a parsed Redirect-Host */ +struct redir_host { + struct fd_list chain; + + DiamId_t id; /* malloc'd */ + size_t len; + /* We don't use the following yet because we don't support dynamic new connections + int secure; + uint16_t port; + int l4; + char proto; + */ +}; + +/* Rule data that is stored depending on Redirect-Host-Usage value */ +union matchdata { + /* DONT_CACHE */ + struct { + struct msg * msg; /* The query message for which this rule was created */ + } message; + + /* ALL_SESSION */ + struct { + os0_t s; /* sid */ + size_t l; + } session; + + /* ALL_REALM */ + struct { + os0_t s; /* Destination-Realm AVP data */ + size_t l; + } realm; + + /* REALM_AND_APPLICATION */ + struct { + application_id_t a; /* message's application */ + os0_t s; /* Destination-Realm AVP data */ + size_t l; + } realm_app; + + /* ALL_APPLICATION */ + struct { + application_id_t a; /* message's application */ + } app; + + /* ALL_HOST */ + struct { + os0_t s; /* the Origin-Host of the Redirect message (which may be the next hop or not) */ + size_t l; + } host; + + /* ALL_USER */ + struct { + os0_t s; /* User-Name AVP data */ + size_t l; + } user; +}; + + +/* Structure to store a Redirect indication */ +struct redir_entry { + uint32_t eyec; /* must be REDIR_ENTRY_EYEC, used for debug only */ + + struct { + os0_t s; /* alloc'd, must be freed */ + size_t l; + } from; /* whom this rule was received from (next hop) ? */ + + struct fd_list target_peers_list; /* The list of Redirect-Hosts for this entry */ + + struct timespec timeout; /* When does this entry expires? */ + struct fd_list exp_list; /* chain in the expire_list list, ordered by expiration date, protected by exp_peer_lock */ + + enum redir_h_u type; /* Type of this entry */ + struct fd_list redir_list; /* link in redirects_usages lists. Lists are ordered by the data value. Protected by rw locks */ + union matchdata data; /* The strings are duplicated & must be freed in this structure */ +}; + +/* The array where the redir_entries are stored */ +struct redir_line { + enum fd_rt_out_score score; + pthread_rwlock_t lock; /* protect the list */ + struct fd_list sentinel; /* list of redir_entry, the "o" field of the sentinel points to the redir_line entry */ +}; +extern struct redir_line redirects_usages[]; + +/* Accelerator to the line lock */ +#define RWLOCK_REDIR( _entry ) ( &(redirects_usages[(_entry)->type].lock) ) + +/* Lock that must be owned before calling some functions */ +extern pthread_mutex_t redir_exp_peer_lock; + +/* Dictionary cached objects */ +extern struct dict_object * redir_dict_dr; +extern struct dict_object * redir_dict_un; + +/* Functions on redir_entry */ +int redir_entry_init(); +int redir_entry_new(struct redir_entry ** e, struct fd_list * targets, uint32_t rhu, struct msg * qry, DiamId_t nh, size_t nhlen, os0_t oh, size_t ohlen); +extern int (*redir_entry_cmp_key[])(union matchdata * , union matchdata *); /* compare functions */ +int redir_entry_insert(struct redir_entry * e); +int redir_entry_destroy(struct redir_entry * e); + +/* Functions for expiry */ +void * redir_exp_thr_fct(void * arg); +int redir_exp_set(struct redir_entry * e, uint32_t duration); + +/* Forward cb */ +int redir_fwd_cb(void * cbdata, struct msg ** msg); + +/* Out callback */ +int redir_out_cb(void * cbdata, struct msg * msg, struct fd_list * candidates); diff -r 4784f2cf325f -r 571b3abaa5df include/freeDiameter/libfdcore.h --- a/include/freeDiameter/libfdcore.h Thu Feb 17 16:37:15 2011 +0900 +++ b/include/freeDiameter/libfdcore.h Fri Feb 18 14:50:37 2011 +0900 @@ -657,6 +657,7 @@ enum fd_rt_out_score { FD_SCORE_NO_DELIVERY = -70, /* We should not send this message to this candidate */ + FD_SCORE_SENT_REDIRECT = -60, /* If this peer previously sent a Redirect indication that applies to this message */ FD_SCORE_INI = -2, /* All candidates are initialized with this value */ FD_SCORE_LOAD_BALANCE = 1, /* Use this to differentiate between several peers with the same score */ FD_SCORE_DEFAULT = 5, /* The peer is a default route for all messages */ @@ -668,6 +669,7 @@ FD_SCORE_REDIR_REALM_APP = 40, /* If there is a redirect rule with REALM_AND_APPLICATION for these message and peer */ FD_SCORE_REDIR_USER = 45, /* If there is a redirect rule with ALL_USER for these message and peer */ FD_SCORE_REDIR_SESSION = 50, /* If there is a redirect rule with ALL_SESSION for these message and peer */ + FD_SCORE_REDIR_ONCE = 55, /* If there is a redirect rule with DONT_CACHE for these message and peer */ FD_SCORE_FINALDEST = 100 /* If the peer is the final recipient of the message (i.e. matching Destination-Host), it receives a big score. */ }; diff -r 4784f2cf325f -r 571b3abaa5df include/freeDiameter/libfdproto.h --- a/include/freeDiameter/libfdproto.h Thu Feb 17 16:37:15 2011 +0900 +++ b/include/freeDiameter/libfdproto.h Fri Feb 18 14:50:37 2011 +0900 @@ -606,8 +606,11 @@ rfc3588 states explicitely that such a Diameter Identity consists only of ASCII characters. */ int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz); -/* This checks a string is a valid DiameterIdentity and applies IDNA transformations otherwise (xn--...) */ -int fd_os_validate_DiameterIdentity(char ** id, size_t * outsz, int memory /* 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static) */ ); +/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it + if *inoutsz is != 0 on entry, *id may not be \0-terminated. + memory has the following meaning: 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static) +*/ +int fd_os_validate_DiameterIdentity(char ** id, size_t * inoutsz, int memory); /* Create an order relationship for binary strings (not needed to be \0 terminated). It does NOT mimic strings relationships so that it is more efficient. It is case sensitive. @@ -626,6 +629,15 @@ int fd_os_almostcasecmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz); #define fd_os_almostcasecmp(_o1, _l1, _o2, _l2) fd_os_almostcasecmp_int((os0_t)(_o1), _l1, (os0_t)(_o2), _l2) +/* Analyze a DiameterURI and return its components. + Return EINVAL if the URI is not valid. + *diamid is malloc'd on function return and must be freed (it is processed by fd_os_validate_DiameterIdentity). + *secure is 0 (no security) or 1 (security enabled) on return. + *port is 0 (default) or a value in host byte order on return. + *transport is 0 (default) or IPPROTO_* on return. + *proto is 0 (default) or 'd' (diameter), 'r' (radius), or 't' (tacacs+) on return. + */ +int fd_os_parse_DiameterURI(uint8_t * uri, size_t urisz, DiamId_t * diamid, size_t * diamidlen, int * secure, uint16_t * port, int * transport, char *proto); /*============================================================*/ /* THREADS */ diff -r 4784f2cf325f -r 571b3abaa5df libfdcore/routing_dispatch.c --- a/libfdcore/routing_dispatch.c Thu Feb 17 16:37:15 2011 +0900 +++ b/libfdcore/routing_dispatch.c Fri Feb 18 14:50:37 2011 +0900 @@ -721,7 +721,7 @@ CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) ); return 0; } - + /* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */ } @@ -1105,8 +1105,6 @@ CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) ); CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) ); - TODO("built-in callbacks for Redirect messages?"); - return 0; } diff -r 4784f2cf325f -r 571b3abaa5df libfdproto/ostr.c --- a/libfdproto/ostr.c Thu Feb 17 16:37:15 2011 +0900 +++ b/libfdproto/ostr.c Fri Feb 18 14:50:37 2011 +0900 @@ -166,16 +166,25 @@ return 1; } -/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it */ -int fd_os_validate_DiameterIdentity(char ** id, size_t * outsz, int memory /* 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static) */) +/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it + if *inoutsz is != 0 on entry, *id may not be \0-terminated. + memory has the following meaning: 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static) +*/ +int fd_os_validate_DiameterIdentity(char ** id, size_t * inoutsz, int memory) { - TRACE_ENTRY("%p %p", id, outsz); + int gotsize = 0; + + TRACE_ENTRY("%p %p", id, inoutsz); + CHECK_PARAMS( id && *id && inoutsz ); - *outsz = strlen(*id); + if (!*inoutsz) + *inoutsz = strlen(*id); + else + gotsize = 1; #ifndef DIAMID_IDNA_IGNORE - if (!fd_os_is_valid_DiameterIdentity((os0_t)*id, *outsz)) { + if (!fd_os_is_valid_DiameterIdentity((os0_t)*id, *inoutsz)) { #ifdef DIAMID_IDNA_REJECT @@ -188,13 +197,23 @@ char *processed; int ret; + if (gotsize) { /* make it \0-terminated */ + if (memory) { + CHECK_MALLOC( *id = os0dup(*id, *inoutsz) ); + memory = 0; + } else { + CHECK_MALLOC( *id = realloc(*id, *inoutsz + 1) ); + (*id)[*inoutsz] = '0'; + } + } + ret = idna_to_ascii_8z ( *id, &processed, IDNA_USE_STD3_ASCII_RULES ); if (ret == IDNA_SUCCESS) { TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity, it was changed to '%s'", *id, processed); if (memory == 0) free(*id); *id = processed; - *outsz = strlen(processed); + *inoutsz = strlen(processed); /* Done! */ } else { TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity and cannot be sanitanized: %s", *id, idna_strerror (ret)); @@ -206,12 +225,197 @@ #endif /* ! DIAMID_IDNA_IGNORE */ { if (memory == 1) { - CHECK_MALLOC( *id = os0dup(*id, *outsz) ); + CHECK_MALLOC( *id = os0dup(*id, *inoutsz) ); } } return 0; } +/* Analyze a DiameterURI and return its components. + Return EINVAL if the URI is not valid. + *diamid is malloc'd on function return and must be freed (it is processed by fd_os_validate_DiameterIdentity). + *secure is 0 (no security) or 1 (security enabled) on return. + *port is 0 (default) or a value in host byte order on return. + *transport is 0 (default) or IPPROTO_* on return. + *proto is 0 (default) or 'd' (diameter), 'r' (radius), or 't' (tacacs+) on return. + */ +int fd_os_parse_DiameterURI(uint8_t * uri, size_t urisz, DiamId_t * diamid, size_t * diamidlen, int * secure, uint16_t * port, int * transport, char *proto) +{ + size_t offset = 0; + DiamId_t fqdn = NULL; + size_t fqdnlen; + TRACE_ENTRY("%p %zd %p %p %p %p %p %p", uri, urisz, diamid, diamidlen, secure, port, transport, proto); + CHECK_PARAMS( uri && urisz ); + + CHECK_PARAMS( urisz > 7 ); /* "aaa" + "://" + something else at least */ + + /* Initialize values */ + if (secure) + *secure = 0; + if (port) + *port = 0; + if (transport) + *transport = 0; + if (proto) + *proto = 0; + + /* Check the beginning */ + if (memcmp( uri, "aaa", 3)) { + TRACE_DEBUG(INFO, "Invalid DiameterURI prefix: got '%.*s', expected 'aaa'", 3, uri); + return EINVAL; + } + offset += 3; + + /* Secure? */ + if (uri[offset] == (uint8_t)'s') { + if (secure) + *secure = 1; + offset += 1; + } + + /* Remaining of URI marker */ + if (memcmp( uri + offset, "://", 3)) { + TRACE_DEBUG(INFO, "Invalid DiameterURI prefix: got '%.*s', expected 'aaa://' or 'aaas://'", offset + 3, uri); + return EINVAL; + } + offset += 3; + + /* This is the start of the FQDN */ + fqdn = (DiamId_t)uri + offset; + for ( ; offset < urisz ; offset++ ) { + /* Stop only when we find ':' or ';' */ + if ((uri[offset] == (uint8_t)':') || (uri[offset] == (uint8_t)';')) + break; + } + fqdnlen = offset - (fqdn - (DiamId_t)uri); + CHECK_FCT(fd_os_validate_DiameterIdentity(&fqdn, &fqdnlen, 1)); + if (diamid) + *diamid = fqdn; + else + free(fqdn); + if (diamidlen) + *diamidlen = fqdnlen; + + if (offset == urisz) + return 0; /* Finished */ + + /* Is there a port ? */ + if (uri[offset] == ':') { + uint16_t p = 0; + do { + offset++; + + if (offset == urisz) + break; + + uint32_t t = (uint32_t)((char)uri[offset] - '0'); + if (t > 9) + break; /* we did not get a digit */ + + t += p * 10; /* the port is specified in decimal base */ + + if (t >= (1<<16)) { + TRACE_DEBUG(INFO, "Invalid DiameterURI: port value is too big, ignored."); + p = 0; + break; + } + + p = t; + } while (1); + + if (port) + *port = p; + } + + if (offset == urisz) + return 0; /* Finished */ + + /* Is there a transport? */ + if ( (urisz - offset > CONSTSTRLEN(";transport=")) + && !strncasecmp((char *)uri + offset, ";transport=", CONSTSTRLEN(";transport=")) ) { + + offset += CONSTSTRLEN(";transport="); + + if (urisz - offset < 3) { + TRACE_DEBUG(INFO, "Invalid DiameterURI: transport string is too short, ignored."); + return 0; + } + if (!strncasecmp((char *)uri + offset, "tcp", CONSTSTRLEN("tcp"))) { + if (transport) + *transport = IPPROTO_TCP; + offset += CONSTSTRLEN("tcp"); + goto after_transport; + } + if (!strncasecmp((char *)uri + offset, "udp", CONSTSTRLEN("udp"))) { + if (transport) + *transport = IPPROTO_UDP; + offset += CONSTSTRLEN("udp"); + goto after_transport; + } + if ((urisz - offset > 3) && !strncasecmp((char *)uri + offset, "sctp", CONSTSTRLEN("sctp"))) { + if (transport) { +#ifndef DISABLE_SCTP + *transport = IPPROTO_SCTP; +#else /* DISABLE_SCTP */ + TRACE_DEBUG(INFO, "Received DiameterURI with 'transport=sctp' but DISABLE_SCTP was selected"); + *transport = 0; +#endif /* DISABLE_SCTP */ + } + offset += CONSTSTRLEN("sctp"); + goto after_transport; + } + + TRACE_DEBUG(INFO, "Invalid DiameterURI: transport string is not recognized ('%.*s'), ignored.", urisz - offset, uri + offset); + + /* eat the remaining of invalid transport until a ';' */ + for (; (offset < urisz) && ((char)uri[offset] != ';'); offset++); + } +after_transport: + if (offset == urisz) + return 0; /* Finished */ + + /* Is there a protocol? */ + if ( ((urisz - offset) > CONSTSTRLEN(";protocol=")) + && (!strncasecmp((char *)uri + offset, ";protocol=", CONSTSTRLEN(";protocol="))) ) { + + offset += CONSTSTRLEN(";protocol="); + + if ( ((urisz - offset) >= CONSTSTRLEN("diameter")) + && (!strncasecmp((char *)uri + offset, "diameter", CONSTSTRLEN("diameter"))) ) { + if (proto) + *proto = 'd'; + offset += CONSTSTRLEN("diameter"); + goto after_proto; + } + + if ( ((urisz - offset) >= CONSTSTRLEN("radius")) + && (!strncasecmp((char *)uri + offset, "radius", CONSTSTRLEN("radius"))) ) { + if (proto) + *proto = 'r'; + offset += CONSTSTRLEN("radius"); + goto after_proto; + } + + if ( ((urisz - offset) >= CONSTSTRLEN("tacacs+")) + && (!strncasecmp((char *)uri + offset, "tacacs+", CONSTSTRLEN("tacacs+"))) ) { + if (proto) + *proto = 't'; + offset += CONSTSTRLEN("tacacs+"); + goto after_proto; + } + + TRACE_DEBUG(INFO, "Invalid DiameterURI: protocol string is not recognized ('%.*s'), ignored.", urisz - offset, uri + offset); + + /* eat the remaining of invalid transport until a ';' */ + for (; (offset < urisz) && ((char)uri[offset] != ';'); offset++); + } +after_proto: + if (offset == urisz) + return 0; /* Finished */ + + TRACE_DEBUG(INFO, "Invalid DiameterURI: string is not recognized ('%.*s'), ignored.", urisz - offset, uri + offset); + return 0; +} /********************************************************************************************************/ diff -r 4784f2cf325f -r 571b3abaa5df libfdproto/rt_data.c --- a/libfdproto/rt_data.c Thu Feb 17 16:37:15 2011 +0900 +++ b/libfdproto/rt_data.c Fri Feb 18 14:50:37 2011 +0900 @@ -131,7 +131,7 @@ /* Create the new entry */ CHECK_MALLOC( new = malloc(sizeof(struct rtd_candidate)) ); memset(new, 0, sizeof(struct rtd_candidate) ); - fd_list_init(&new->chain, NULL); + fd_list_init(&new->chain, new); CHECK_MALLOC( new->diamid = os0dup(peerid, peeridlen) ) new->diamidlen = peeridlen; if (realm) { diff -r 4784f2cf325f -r 571b3abaa5df tests/testostr.c --- a/tests/testostr.c Thu Feb 17 16:37:15 2011 +0900 +++ b/tests/testostr.c Fri Feb 18 14:50:37 2011 +0900 @@ -74,7 +74,7 @@ /* Check the Diameter Identity functions */ { char * res; - size_t len; + size_t len=0; /* A valid ASCII domain name */ res = TEST_IDN_CONV; @@ -87,6 +87,7 @@ /* Now, an invalid string */ res = TEST_IDN_UTF8; + len = 0; #ifdef DIAMID_IDNA_IGNORE