Navigation


Changeset 706:4ffbc9f1e922 in freeDiameter for libfdcore/routing_dispatch.c


Ignore:
Timestamp:
Feb 9, 2011, 3:26:58 PM (13 years ago)
Author:
Sebastien Decugis <sdecugis@nict.go.jp>
Branch:
default
Phase:
public
Message:

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.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libfdcore/routing_dispatch.c

    r691 r706  
    208208                struct fd_peer * peer;
    209209                struct fd_app *found;
    210                 CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
    211                 if (peer && (peer->p_hdr.info.runtime.pir_relay == 0)) {
     210                CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
     211                if (peer && !peer->p_hdr.info.runtime.pir_relay) {
    212212                        /* Check if the remote peer advertised the message's appli */
    213213                        CHECK_FCT( fd_app_check(&peer->p_hdr.info.runtime.pir_apps, hdr->msg_appl, &found) );
     
    264264        for (li = candidates->next; li != candidates; li = li->next) {
    265265                struct rtd_candidate *c = (struct rtd_candidate *) li;
     266               
     267            #if 0 /* this is actually useless since the sending process will also ensure that the peer is still available */
    266268                struct fd_peer * peer;
    267                 CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
    268                 if (peer) {
    269                         if (dh
    270                                 && (dh->os.len == strlen(peer->p_hdr.info.pi_diamid))
    271                                 /* Here again we use strncasecmp on UTF8 data... This should probably be changed. */
    272                                 && (strncasecmp(peer->p_hdr.info.pi_diamid, (char *)dh->os.data, dh->os.len) == 0)) {
    273                                 /* The candidate is the Destination-Host */
    274                                 c->score += FD_SCORE_FINALDEST;
    275                         } else {
    276                                 if (dr  && peer->p_hdr.info.runtime.pir_realm
    277                                         && (dr->os.len == strlen(peer->p_hdr.info.runtime.pir_realm))
    278                                         /* Yet another case where we use strncasecmp on UTF8 data... Hmmm :-( */
    279                                         && (strncasecmp(peer->p_hdr.info.runtime.pir_realm, (char *)dr->os.data, dr->os.len) == 0)) {
    280                                         /* The candidate's realm matchs the Destination-Realm */
    281                                         c->score += FD_SCORE_REALM;
    282                                 }
     269                /* Since the candidates list comes from the peers list, we do not have any issue with upper/lower case to find the peer object */
     270                CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
     271                if (!peer)
     272                        continue; /* it has been deleted since the candidate list was generated; avoid sending to this one in that case. */
     273            #endif /* 0 */
     274               
     275                /* In the AVPs, the value comes from the network, so let's be case permissive */
     276                if (dh && !fd_os_almostcasecmp(dh->os.data, dh->os.len, c->diamid, c->diamidlen) ) {
     277                        /* The candidate is the Destination-Host */
     278                        c->score += FD_SCORE_FINALDEST;
     279                } else {
     280                        if (dr && !fd_os_almostcasecmp(dr->os.data, dr->os.len, c->realm, c->realmlen) ) {
     281                                /* The candidate's realm matchs the Destination-Realm */
     282                                c->score += FD_SCORE_REALM;
    283283                        }
    284284                }
     
    298298       
    299299        TRACE_ENTRY("%p %p %p", un, excl_idx, at_idx);
    300         CHECK_PARAMS_DO( un && excl_idx, return );
     300        CHECK_PARAMS_DO( un && excl_idx && at_idx, return );
     301       
    301302        *excl_idx = 0;
     303        *at_idx = 0;
    302304       
    303305        /* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */
     
    307309                        if (!*excl_idx)
    308310                                *excl_idx = i;
    309                         if (!at_idx)
    310                                 return;
     311                        continue;
    311312                }
    312313                /* If we reach the realm part, we can stop */
    313314                if ( un->os.data[i] == (unsigned char) '@' ) {
    314                         if (at_idx)
    315                                 *at_idx = i;
     315                        *at_idx = i;
    316316                        break;
     317                }
     318                /* Stop if we find a \0 in the middle */
     319                if ( un->os.data[i] == 0 ) {
     320                        return;
    317321                }
    318322                /* Skip escaped characters */
     
    321325                        continue;
    322326                }
    323                 /* Skip UTF-8 characters spanning on several bytes */
    324                 if ( (un->os.data[i] & 0xF8) == 0xF0 ) { /* 11110zzz */
    325                         i += 3;
    326                         continue;
    327                 }
    328                 if ( (un->os.data[i] & 0xF0) == 0xE0 ) { /* 1110yyyy */
    329                         i += 2;
    330                         continue;
    331                 }
    332                 if ( (un->os.data[i] & 0xE0) == 0xC0 ) { /* 110yyyxx */
    333                         i += 1;
    334                         continue;
    335                 }
    336327        }
    337328       
     
    340331
    341332/* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, RFC5729 */
    342 static int is_decorated_NAI(union avp_value * un)
    343 {
    344         int i;
    345         TRACE_ENTRY("%p", un);
    346        
    347         /* If there was no User-Name, we return false */
    348         if (un == NULL)
    349                 return 0;
    350        
    351         nai_get_indexes(un, &i, NULL);
    352        
    353         return i;
    354 }
    355 
    356333/* Create new User-Name and Destination-Realm values */
    357 static int process_decorated_NAI(union avp_value * un, union avp_value * dr)
    358 {
    359         int at_idx = 0, sep_idx = 0;
     334static int process_decorated_NAI(int * was_nai, union avp_value * un, union avp_value * dr)
     335{
     336        int at_idx, sep_idx;
    360337        unsigned char * old_un;
    361         TRACE_ENTRY("%p %p", un, dr);
    362         CHECK_PARAMS(un && dr);
     338        TRACE_ENTRY("%p %p %p", was_nai, un, dr);
     339        CHECK_PARAMS(was_nai && un && dr);
    363340       
    364341        /* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */
     
    367344        /* Search the positions of the first '!' and the '@' in the string */
    368345        nai_get_indexes(un, &sep_idx, &at_idx);
    369         CHECK_PARAMS( (0 < sep_idx) && (sep_idx < at_idx) && (at_idx < un->os.len));
     346        if ((!sep_idx) || (sep_idx > at_idx) || !fd_os_is_valid_DiameterIdentity(old_un, sep_idx /* this is the new realm part */)) {
     347                *was_nai = 0;
     348                return 0;
     349        }
     350       
     351        *was_nai = 1;
    370352       
    371353        /* Create the new User-Name value */
     
    390372}
    391373
     374
    392375/* Function to return an error to an incoming request */
    393376static int return_error(struct msg ** pmsg, char * error_code, char * error_message, struct avp * failedavp)
     
    398381        /* Get the source of the message */
    399382        {
    400                 char * id;
    401                 CHECK_FCT( fd_msg_source_get( *pmsg, &id ) );
     383                DiamId_t id;
     384                size_t   idlen;
     385                CHECK_FCT( fd_msg_source_get( *pmsg, &id, &idlen ) );
    402386               
    403387                if (id == NULL) {
     
    406390               
    407391                        /* Search the peer with this id */
    408                         CHECK_FCT( fd_peer_getbyid( id, (void *)&peer ) );
     392                        CHECK_FCT( fd_peer_getbyid( id, idlen, 0, (void *)&peer ) );
    409393
    410394                        if (!peer) {
     
    439423/****************************************************************************/
    440424
    441 /* These are the functions of each threads: dispatch & routing */
    442425/* The DISPATCH message processing */
    443426static int msg_dispatch(struct msg ** pmsg)
     
    447430        struct session * sess;
    448431        enum disp_action action;
    449         const char * ec = NULL;
    450         const char * em = NULL;
     432        char * ec = NULL;
     433        char * em = NULL;
    451434
    452435        /* Read the message header */
     
    460443        CHECK_FCT_DO( ret = fd_msg_parse_or_error( pmsg ),
    461444                {
    462                         /* in case of error, the message is already dump'd */
     445                        /* in case of error */
    463446                        if ((ret == EBADMSG) && (*pmsg != NULL)) {
    464447                                /* msg now contains the answer message to send back */
     
    512495                                em = "The message was not handled by any extension callback";
    513496                                ec = "DIAMETER_COMMAND_UNSUPPORTED";
    514                        
     497                                /* and continue as if an error occurred... */
    515498                        case DISP_ACT_ERROR:
    516499                                /* We have a problem with delivering the message */
     
    528511                                /* Create an answer with the error code and message */
    529512                                CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, 0 ) );
    530                                 CHECK_FCT( fd_msg_rescode_set(*pmsg, (char *)ec, (char *)em, NULL, 1 ) );
     513                                CHECK_FCT( fd_msg_rescode_set(*pmsg, ec, em, NULL, 1 ) );
    531514                               
    532515                        case DISP_ACT_SEND:
     
    545528        int is_req = 0;
    546529        int is_err = 0;
    547         char * qry_src = NULL;
     530        DiamId_t qry_src = NULL;
     531        size_t   qry_src_len = 0;
    548532
    549533        /* Read the message header */
     
    572556                /* Check if we have local support for the message application */
    573557                if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) {
    574                         TRACE_DEBUG(INFO, "Received a routable message with application id 0, returning DIAMETER_APPLICATION_UNSUPPORTED");
     558                        TRACE_DEBUG(INFO, "Received a routable message with application id 0 or " _stringize(AI_RELAY) " (relay),\n"
     559                                          " returning DIAMETER_APPLICATION_UNSUPPORTED");
    575560                        CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL) );
    576561                        return 0;
     
    587572                        struct fd_pei error_info;
    588573                        int ret;
     574                       
     575                        memset(&error_info, 0, sizeof(struct fd_pei));
     576                       
    589577                        CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
    590578
     
    604592                                                ASSERT( ahdr->avp_value );
    605593                                                /* Compare the Destination-Host AVP of the message with our identity */
    606                                                 if (ahdr->avp_value->os.len != fd_g_config->cnf_diamid_len) {
     594                                                if (!fd_os_almostcasecmp(ahdr->avp_value->os.data, ahdr->avp_value->os.len, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len)) {
     595                                                        is_dest_host = YES;
     596                                                } else {
    607597                                                        is_dest_host = NO;
    608                                                 } else {
    609                                                         is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len)
    610                                                                                 ? NO : YES);
    611598                                                }
    612599                                                break;
     
    626613                                                dr_val = ahdr->avp_value;
    627614                                                /* Compare the Destination-Realm AVP of the message with our identity */
    628                                                 if (ahdr->avp_value->os.len != fd_g_config->cnf_diamrlm_len) {
     615                                                if (!fd_os_almostcasecmp(dr_val->os.data, dr_val->os.len, fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len)) {
     616                                                        is_dest_realm = YES;
     617                                                } else {
    629618                                                        is_dest_realm = NO;
    630                                                 } else {
    631                                                         is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len)
    632                                                                                 ? NO : YES);
    633619                                                }
    634620                                                break;
    635621
     622                                        /* we also use User-Name for decorated NAI */
    636623                                        case AC_USER_NAME:
    637624                                                /* Parse this AVP */
     
    652639                        }
    653640
     641                        /* Stop when we found all 3 AVPs -- they are supposed to be at the beginning of the message, so this should be fast */
    654642                        if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un)
    655643                                break;
     
    682670                if ((is_dest_host == NO) || (is_dest_realm == NO)) {
    683671                        if (fd_g_config->cnf_flags.no_fwd) {
    684                                 CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "This peer is not an agent", NULL) );
     672                                CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "I am not a Diameter agent", NULL) );
    685673                                return 0;
    686674                        }
    687675                } else {
    688676                /* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */
     677                        int is_nai = 0;
    689678
    690679                        /* test for decorated NAI  (RFC5729 section 4.4) */
    691                         if (is_decorated_NAI(un_val)) {
    692                                 /* Handle the decorated NAI */
    693                                 CHECK_FCT_DO( process_decorated_NAI(un_val, dr_val),
    694                                         {
    695                                                 /* If the process failed, we assume it is because of the AVP format */
    696                                                 CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) );
    697                                                 return 0;
    698                                         } );
    699 
     680                        /* Handle the decorated NAI */
     681                        CHECK_FCT_DO( process_decorated_NAI(&is_nai, un_val, dr_val),
     682                                {
     683                                        /* If the process failed, we assume it is because of the AVP format */
     684                                        CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) );
     685                                        return 0;
     686                                } );
     687                               
     688                        if (is_nai) {
    700689                                /* We have transformed the AVP, now submit it again in the queue */
    701690                                CHECK_FCT(fd_fifo_post(fd_g_incoming, pmsg) );
     
    724713                /* Retrieve the corresponding query and its origin */
    725714                CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
    726                 CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
     715                CHECK_FCT( fd_msg_source_get( qry, &qry_src, &qry_src_len ) );
    727716
    728717                if ((!qry_src) && (!is_err)) {
     
    733722
    734723                /* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */
     724                TODO("Callback for answers with a Redirect code?");
    735725        }
    736726
     
    800790        if ( ! is_req ) {
    801791                struct msg * qry;
    802                 char * qry_src = NULL;
     792                DiamId_t qry_src = NULL;
     793                size_t qry_src_len = 0;
    803794                struct msg_hdr * qry_hdr;
    804795                struct fd_peer * peer = NULL;
     
    806797                /* Retrieve the corresponding query and its origin */
    807798                CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
    808                 CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
     799                CHECK_FCT( fd_msg_source_get( qry, &qry_src, &qry_src_len ) );
    809800
    810801                ASSERT( qry_src ); /* if it is NULL, the message should have been in the LOCAL queue! */
    811802
    812803                /* Find the peer corresponding to this name */
    813                 CHECK_FCT( fd_peer_getbyid( qry_src, (void *) &peer ) );
    814                 fd_cpu_flush_cache();
    815                 if ((!peer) || (peer->p_hdr.info.runtime.pir_state != STATE_OPEN)) {
     804                CHECK_FCT( fd_peer_getbyid( qry_src, qry_src_len, 0, (void *) &peer ) );
     805                if (fd_peer_getstate(peer) != STATE_OPEN) {
    816806                        fd_msg_log( FD_MSG_LOG_DROPPED, *pmsg, "Unable to forward answer to deleted / closed peer '%s'.", qry_src);
    817807                        fd_msg_free(*pmsg);
     
    844834                for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
    845835                        struct fd_peer * p = (struct fd_peer *)li->o;
    846                         CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, p->p_hdr.info.pi_diamid, p->p_hdr.info.runtime.pir_realm), { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
     836                        CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd,
     837                                                        p->p_hdr.info.pi_diamid,
     838                                                        p->p_hdr.info.pi_diamidlen,
     839                                                        p->p_hdr.info.runtime.pir_realm,
     840                                                        p->p_hdr.info.runtime.pir_realmlen),
     841                                { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
    847842                }
    848843                CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
     
    867862                                        } );
    868863                                ASSERT( ahdr->avp_value );
    869                                 /* Remove this value from the list */
    870                                 fd_rtd_candidate_del(rtd, (char *)ahdr->avp_value->os.data, ahdr->avp_value->os.len);
     864                                /* Remove this value from the list. We don't need to pay special attention to the contents here. */
     865                                fd_rtd_candidate_del(rtd, ahdr->avp_value->os.data, ahdr->avp_value->os.len);
    871866                        }
    872867
     
    876871        }
    877872
    878         /* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? */
     873        /* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? -- TODO */
    879874
    880875        /* Ok, we have our list in rtd now, let's (re)initialize the scores */
     
    928923
    929924                /* Search for the peer */
    930                 CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
    931 
    932                 fd_cpu_flush_cache();
    933                 if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
     925                CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
     926
     927                if (fd_peer_getstate(peer) == STATE_OPEN) {
    934928                        /* Send to this one */
    935929                        CHECK_FCT_DO( fd_out_send(pmsg, NULL, peer, 0), continue );
     
    964958/* Control of the threads */
    965959static enum { RUN = 0, STOP = 1 } order_val = RUN;
    966 static pthread_mutex_t order_lock = PTHREAD_MUTEX_INITIALIZER;
     960static pthread_mutex_t order_state_lock = PTHREAD_MUTEX_INITIALIZER;
    967961
    968962/* Threads report their status */
     
    970964static void cleanup_state(void * state_loc)
    971965{
    972         if (state_loc)
    973                 *(enum thread_state *)state_loc = NOTRUNNING;
     966        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
     967        *(enum thread_state *)state_loc = NOTRUNNING;
     968        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
    974969}
    975970
     
    991986       
    992987        /* Mark the thread running */
     988        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
    993989        *(enum thread_state *)arg = RUNNING;
    994         fd_cpu_flush_cache();
     990        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
    995991       
    996992        do {
     
    1000996                {
    1001997                        int must_stop;
    1002                         CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), { ASSERT(0); } ); /* we lock to flush the caches */
     998                        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), { ASSERT(0); } ); /* we lock to flush the caches */
    1003999                        must_stop = (order_val == STOP);
    1004                         CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), { ASSERT(0); } );
     1000                        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), { ASSERT(0); } );
    10051001                        if (must_stop)
    10061002                                goto end;
     
    10141010                {
    10151011                        int ret;
    1016                         ret = fd_fifo_get ( queue, &msg );
     1012                        struct timespec ts;
     1013                       
     1014                        CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), goto fatal_error );
     1015                        ts.tv_sec += 1;
     1016                       
     1017                        ret = fd_fifo_timedget ( queue, &msg, &ts );
     1018                        if (ret == ETIMEDOUT)
     1019                                /* loop, check if the thread must stop now */
     1020                                continue;
    10171021                        if (ret == EPIPE)
    10181022                                /* The queue was destroyed, we are probably exiting */
     
    11001104        CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) );
    11011105        CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) );
     1106       
     1107        TODO("built-in callbacks for Redirect messages?");
     1108       
    11021109        return 0;
    11031110}
     
    11061113int fd_rtdisp_cleanstop(void)
    11071114{
    1108         CHECK_POSIX( pthread_mutex_lock(&order_lock) );
     1115        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
    11091116        order_val = STOP;
    1110         CHECK_POSIX( pthread_mutex_unlock(&order_lock) );
     1117        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
    11111118
    11121119        return 0;
     
    11171124        TRACE_ENTRY("%p %p", st, thr);
    11181125        CHECK_PARAMS_DO(st && thr, return);
     1126        int terminated;
     1127       
     1128        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
     1129        terminated = (*st == NOTRUNNING);
     1130        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
     1131       
    11191132
    11201133        /* Wait for a second for the thread to complete, by monitoring my_state */
    1121         fd_cpu_flush_cache();
    1122         if (*st != NOTRUNNING) {
     1134        if (!terminated) {
    11231135                TRACE_DEBUG(INFO, "Waiting for the %s thread to have a chance to terminate", th_name);
    11241136                do {
     
    11311143                       
    11321144                        while (TS_IS_INFERIOR( &ts, &ts_final )) {
    1133                                 fd_cpu_flush_cache();
    1134                                 if (*st == NOTRUNNING)
     1145                       
     1146                                CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
     1147                                terminated = (*st == NOTRUNNING);
     1148                                CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
     1149                                if (terminated)
    11351150                                        break;
    11361151                               
Note: See TracChangeset for help on using the changeset viewer.