Commit deb10130 authored by Sebastien Decugis's avatar Sebastien Decugis
Browse files

Support for Diameter Redirects through rt_redirect.fdx extension (EXPERIMENTAL)

parent 5fce4362
......@@ -19,8 +19,9 @@ freediameter (1.1.0) UNRELEASED; urgency=low
* 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 <sdecugis@nict.go.jp> Mon, 07 Feb 2011 17:24:20 +0900
-- Sebastien Decugis <sdecugis@nict.go.jp> Fri, 18 Feb 2011 14:47:24 +0900
freediameter (1.0.4) UNRELEASED; urgency=low
......
......@@ -61,8 +61,9 @@ FD_EXTENSION_SUBDIR(app_sip "Diameter SIP Authentication and Authorization s
####
# 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)
####
......
......@@ -214,7 +214,7 @@ static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, u
{
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 rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client
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 @@ int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client
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));
......
......@@ -594,7 +594,7 @@ static int sip_rad_req( struct rgwp_config * cs, struct session ** session, stru
{
//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);
......
# 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)
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@nict.go.jp> *
* *
* 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;
}
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@nict.go.jp> *
* *
* 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;
}
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@nict.go.jp> *
* *
* 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;