Mercurial > hg > freeDiameter
changeset 87:c1c0f8a45c67
Backup
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Fri, 04 Dec 2009 17:23:06 +0900 |
parents | e3e22d89e023 |
children | 9e2db1647d6f |
files | freeDiameter/apps.c freeDiameter/dispatch.c freeDiameter/p_ce.c freeDiameter/routing.c include/freeDiameter/freeDiameter.h include/freeDiameter/libfreeDiameter.h |
diffstat | 6 files changed, 374 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/freeDiameter/apps.c Thu Dec 03 17:36:35 2009 +0900 +++ b/freeDiameter/apps.c Fri Dec 04 17:23:06 2009 +0900 @@ -76,16 +76,26 @@ return 0; } -/* Tag any common application between target and reference (into target) */ -int fd_app_find_common(struct fd_list * target, struct fd_list * reference) +/* Check if a given application id is in a list */ +int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail) { + struct fd_list * li; + TRACE_ENTRY("%p %d %p", list, aid, detail); + CHECK_PARAMS(list && detail); + *detail = NULL; + + /* Search in the list */ + for (li = list->next; li != list; li = li->next) { + struct fd_app * a = (struct fd_app *)li; + if (a->appid < aid) + continue; + + if (a->appid == aid) + *detail = a; + break; + } + + return 0; } - -/* Return true if at least one app was tagged common, false otherwise */ -int fd_app_gotcommon(struct fd_list * apps) -{ - -} -
--- a/freeDiameter/dispatch.c Thu Dec 03 17:36:35 2009 +0900 +++ b/freeDiameter/dispatch.c Fri Dec 04 17:23:06 2009 +0900 @@ -64,3 +64,8 @@ return fd_app_merge(&fd_g_config->cnf_apps, aid, vid, auth, acct); } + + +/* Note2: if the message is still for local delivery, we should test for duplicate + (draft-asveren-dime-dupcons-00). This may conflict with path validation decisions, no clear answer yet */ +
--- a/freeDiameter/p_ce.c Thu Dec 03 17:36:35 2009 +0900 +++ b/freeDiameter/p_ce.c Fri Dec 04 17:23:06 2009 +0900 @@ -729,6 +729,9 @@ CHECK_FCT( res ); } + /* Check if we have common applications */ + TODO("DIAMETER_NO_COMMON_APPLICATION ?"); + /* Do we send ISI back ? */ if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE)
--- a/freeDiameter/routing.c Thu Dec 03 17:36:35 2009 +0900 +++ b/freeDiameter/routing.c Fri Dec 04 17:23:06 2009 +0900 @@ -190,8 +190,79 @@ ( fd_g_local is managed by the dispatch thread ) to create additional threads if a queue is filling up. */ +/* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, draft-ietf-dime-nai-routing-04 */ +static int is_decorated_NAI(union avp_value * un) +{ + int i; + TRACE_ENTRY("%p", un); + + /* If there was no User-Name, we return false */ + if (un == NULL) + return 0; + + /* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */ + for (i = 0; i < un->os.len; i++) { + if ( un->os.data[i] == (unsigned char) '!' ) + return 1; + if ( un->os.data[i] == (unsigned char) '@' ) + break; + if ( un->os.data[i] == (unsigned char) '\\' ) + i++; /* next one was escaped */ + } + + return 0; +} + +/* Create new User-Name and Destination-Realm values */ +static int process_decorated_NAI(union avp_value * un, union avp_value * dr) +{ + int i, at_idx = 0, sep_idx = 0; + unsigned char * old_un; + TRACE_ENTRY("%p %p", un, dr); + CHECK_PARAMS(un && dr); + + /* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */ + old_un = un->os.data; + + /* Search the positions of the first '!' and the '@' in the string */ + for (i = 0; i < un->os.len; i++) { + if ( (!sep_idx) && (old_un[i] == (unsigned char) '!') ) + sep_idx = i; + if ( old_un[i] == (unsigned char) '@' ) { + at_idx = i; + break; + } + if ( un->os.data[i] == (unsigned char) '\\' ) + i++; /* next one is escaped */ + } + + CHECK_PARAMS( 0 < sep_idx < at_idx < un->os.len); + + /* Create the new User-Name value */ + CHECK_MALLOC( un->os.data = malloc( at_idx ) ); + memcpy( un->os.data, old_un + sep_idx + 1, at_idx - sep_idx ); /* user@ */ + memcpy( un->os.data + at_idx - sep_idx, old_un, sep_idx ); /* homerealm.example.net */ + + /* Create the new Destination-Realm value */ + CHECK_MALLOC( dr->os.data = realloc(dr->os.data, sep_idx) ); + memcpy( dr->os.data, old_un, sep_idx ); + dr->os.len = sep_idx; + + TRACE_DEBUG(FULL, "Processed Decorated NAI '%.*s' into '%.*s' (%.*s)", + un->os.len, old_un, + at_idx, un->os.data, + dr->os.len, dr->os.data); + + un->os.len = at_idx; + free(old_un); + + return 0; +} + + + /* Function to return an error to an incoming request */ -static int return_error(struct msg * msg, char * error_code) +static int return_error(struct msg * msg, char * error_code, char * error_message, struct avp * failedavp) { struct fd_peer * peer; @@ -215,7 +286,7 @@ CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ) ); /* Set the error code */ - CHECK_FCT( fd_msg_rescode_set(msg, error_code, NULL, NULL, 1 ) ); + CHECK_FCT( fd_msg_rescode_set(msg, error_code, error_message, failedavp, 1 ) ); /* Send the answer */ CHECK_FCT( fd_out_send(&msg, NULL, peer) ); @@ -245,6 +316,7 @@ struct msg_hdr * hdr; int is_req = 0; int is_err = 0; + char * qry_src = NULL; /* Test if we were told to stop */ pthread_testcancel(); @@ -264,15 +336,204 @@ /* Handle incorrect bits */ if (is_req && is_err) { - CHECK_FCT_DO( return_error( msg, "DIAMETER_INVALID_HDR_BITS"), goto fatal_error ); + CHECK_FCT_DO( return_error( msg, "DIAMETER_INVALID_HDR_BITS", "R & E bits were set", NULL), goto fatal_error ); continue; } - + /* If it is a request, we must analyze its content to decide what we do with it */ + if (is_req) { + struct avp * avp, *un = NULL; + union avp_value * un_val = NULL, *dr_val = NULL; + enum status { UNKNOWN, YES, NO }; + /* Are we Destination-Host? */ + enum status is_dest_host = UNKNOWN; + /* Are we Destination-Realm? */ + enum status is_dest_realm = UNKNOWN; + /* Do we support the application of the message? */ + enum status is_local_app = UNKNOWN; + + /* Check if we have local support for the message application */ + if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) { + TRACE_DEBUG(INFO, "Received a routable message with application id 0, returning DIAMETER_APPLICATION_UNSUPPORTED"); + CHECK_FCT_DO( return_error( msg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL), goto fatal_error ); + continue; + } else { + struct fd_app * app; + CHECK_FCT_DO( fd_app_check(&fd_g_config->cnf_apps, hdr->msg_appl, &app), goto fatal_error ); + is_local_app = (app ? YES : NO); + } + + /* Parse the message for Dest-Host and Dest-Realm */ + CHECK_FCT_DO( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL), goto fatal_error ); + while (avp) { + struct avp_hdr * ahdr; + CHECK_FCT_DO( fd_msg_avp_hdr( avp, &ahdr ), goto fatal_error ); + + if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) { + switch (ahdr->avp_code) { + case AC_DESTINATION_HOST: + /* Parse this AVP */ + CHECK_FCT_DO( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict ), goto fatal_error ); + ASSERT( ahdr->avp_value ); + /* Compare the Destination-Host AVP of the message with our identity */ + if (ahdr->avp_value->os.len != fd_g_config->cnf_diamid_len) { + is_dest_host = NO; + } else { + is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len) + ? NO : YES); + } + break; + + case AC_DESTINATION_REALM: + /* Parse this AVP */ + CHECK_FCT_DO( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict ), goto fatal_error ); + ASSERT( ahdr->avp_value ); + dr_val = ahdr->avp_value; + /* Compare the Destination-Realm AVP of the message with our identity */ + if (ahdr->avp_value->os.len != fd_g_config->cnf_diamrlm_len) { + is_dest_realm = NO; + } else { + is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len) + ? NO : YES); + } + break; + + case AC_USER_NAME: + /* Parse this AVP */ + CHECK_FCT_DO( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict ), goto fatal_error ); + ASSERT( ahdr->avp_value ); + un = avp; + un_val = ahdr->avp_value; + break; + } + } + + if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un) + break; + + /* Go to next AVP */ + CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), goto fatal_error ); + } + + /* OK, now decide what we do with the request */ + + /* Handle the missing routing AVPs first */ + if ( is_dest_realm == UNKNOWN ) { + CHECK_FCT_DO( return_error( msg, "DIAMETER_COMMAND_UNSUPPORTED", "Non-routable message not supported (invalid bit ? missing Destination-Realm ?)", NULL), goto fatal_error ); + continue; + } + + /* If we are listed as Destination-Host */ + if (is_dest_host == YES) { + if (is_local_app == YES) { + /* Ok, give the message to the dispatch thread */ + CHECK_FCT_DO(fd_fifo_post(fd_g_local, &msg), goto fatal_error ); + } else { + /* We don't support the application, reply an error */ + CHECK_FCT_DO( return_error( msg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL), goto fatal_error ); + } + continue; + } + + /* If the message is explicitely for someone else */ + if ((is_dest_host == NO) || (is_dest_realm == NO)) { + if (fd_g_config->cnf_flags.no_fwd) { + CHECK_FCT_DO( return_error( msg, "DIAMETER_UNABLE_TO_DELIVER", "This peer is not an agent", NULL), goto fatal_error ); + continue; + } + } else { + /* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */ + + /* test for decorated NAI (draft-ietf-dime-nai-routing-04 section 4.4) */ + if (is_decorated_NAI(un_val)) { + /* Handle the decorated NAI */ + CHECK_FCT_DO( process_decorated_NAI(un_val, dr_val), + { + /* If the process failed, we assume it is because of the AVP format */ + CHECK_FCT_DO( return_error( msg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un), goto fatal_error ); + continue; + } ); + + /* We have transformed the AVP, now submit it again in the queue */ + CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto fatal_error ); + continue; + } + + if (is_local_app == YES) { + /* Handle localy since we are able to */ + CHECK_FCT_DO(fd_fifo_post(fd_g_local, &msg), goto fatal_error ); + continue; + } + + if (fd_g_config->cnf_flags.no_fwd) { + /* We return an error */ + CHECK_FCT_DO( return_error( msg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL), goto fatal_error ); + continue; + } + } + + /* From that point, for requests, we will call the registered callbacks, then forward to another peer */ + + } else { + /* The message is an answer */ + struct msg * qry; + + /* Retrieve the corresponding query and its origin */ + CHECK_FCT_DO( fd_msg_answ_getq( msg, &qry ), goto fatal_error ); + CHECK_FCT_DO( fd_msg_source_get( qry, &qry_src ), goto fatal_error ); + + if ((!qry_src) && (!is_err)) { + /* The message is a normal answer to a request issued localy, we do not call the callbacks chain on it. */ + CHECK_FCT_DO(fd_fifo_post(fd_g_local, &msg), goto fatal_error ); + continue; + } + + /* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */ + } - - - + /* Call all registered callbacks for this message */ + { + struct fd_list * li; + + CHECK_FCT_DO( pthread_rwlock_rdlock( &rt_fwd_lock ), goto fatal_error ); + pthread_cleanup_push( fd_cleanup_rwlock, &rt_fwd_lock ); + + /* requests: dir = 1 & 2 => in order; answers = 3 & 2 => in reverse order */ + for ( li = (is_req ? rt_fwd_list.next : rt_fwd_list.prev) ; msg && (li != &rt_fwd_list) ; li = (is_req ? li->next : li->prev) ) { + struct rt_hdl * rh = (struct rt_hdl *)li; + + if (is_req && (rh->dir > RT_FWD_ALL)) + break; + if ((!is_req) && (rh->dir < RT_FWD_ALL)) + break; + + /* Ok, call this cb */ + TRACE_DEBUG(ANNOYING, "Calling next FWD callback on %p : %p", msg, rh->rt_fwd_cb); + CHECK_FCT_DO( (*rh->rt_fwd_cb)(rh->cbdata, &msg), + { + TRACE_DEBUG(INFO, "A FWD routing callback returned an error, message discarded."); + fd_msg_dump_walk(INFO, msg); + fd_msg_free(msg); + msg = NULL; + } ); + } + + pthread_cleanup_pop(0); + CHECK_FCT_DO( pthread_rwlock_unlock( &rt_fwd_lock ), goto fatal_error ); + + /* If a callback has handled the message, we stop now */ + if (!msg) + continue; + } + + /* Now handle the message to the next step: either forward to another peer, or for local delivery */ + if (is_req || qry_src) { + CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &msg), goto fatal_error ); + } else { + CHECK_FCT_DO(fd_fifo_post(fd_g_local, &msg), goto fatal_error ); + } + + /* We're done with this message */ } while (1); fatal_error: @@ -281,11 +542,81 @@ return NULL; } -/* Note: after testing if the message is to be handled locally, we should test for decorated NAI - (draft-ietf-dime-nai-routing-04 section 4.4) */ - -/* Note2: if the message is still for local delivery, we should test for duplicate - (draft-asveren-dime-dupcons-00). This may conflict with path validation decisions, no clear answer yet */ + +/* The (routing-out) thread -- see description in freeDiameter.h */ +static void * routing_out_thr(void * arg) +{ + TRACE_ENTRY("%p", arg); + + /* Set the thread name */ + if (arg) { + char buf[48]; + snprintf(buf, sizeof(buf), "Routing-OUT %p", arg); + fd_log_threadname ( buf ); + } else { + fd_log_threadname ( "Routing-OUT" ); + } + + /* Main thread loop */ + do { + struct msg * msg; + struct msg_hdr * hdr; + int is_req = 0; + + /* Test if we were told to stop */ + pthread_testcancel(); + + /* Get the next message from the ougoing queue */ + CHECK_FCT_DO( fd_fifo_get ( fd_g_outgoing, &msg ), goto fatal_error ); + + if (TRACE_BOOL(FULL)) { + TRACE_DEBUG(FULL, "Picked next message:"); + fd_msg_dump_one(FULL, msg); + } + + /* Read the message header */ + CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto fatal_error ); + is_req = hdr->msg_flags & CMD_FLAG_REQUEST; + + /* For answers, the routing is very easy */ + if ( ! is_req ) { + struct msg * qry; + char * qry_src = NULL; + struct fd_peer * peer = NULL; + + /* Retrieve the corresponding query and its origin */ + CHECK_FCT_DO( fd_msg_answ_getq( msg, &qry ), goto fatal_error ); + CHECK_FCT_DO( fd_msg_source_get( qry, &qry_src ), goto fatal_error ); + + ASSERT( qry_src ); /* if it is NULL, the message should have been in the LOCAL queue! */ + + /* Find the peer corresponding to this name */ + CHECK_FCT_DO( fd_peer_getbyid( qry_src, (void *) &peer ), goto fatal_error ); + if ((!peer) || (peer->p_hdr.info.runtime.pir_state != STATE_OPEN)) { + TRACE_DEBUG(INFO, "Unable to forward answer message to peer '%s', deleted or not in OPEN state.", qry_src); + fd_msg_dump_walk(INFO, msg); + fd_msg_free(msg); + continue; + } + + /* Push the message into this peer */ + CHECK_FCT_DO( fd_out_send(&msg, NULL, peer), goto fatal_error ); + + /* We're done with this answer */ + continue; + } + + /* The message is a request */ + TODO("use struct rt_data and fd_msg_rt_get"); + + /* We're done with this message */ + } while (1); + +fatal_error: + TRACE_DEBUG(INFO, "An error occurred in routing module! OUT thread is terminating..."); + CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); + return NULL; +} @@ -293,6 +624,8 @@ int fd_rt_init(void) { TODO("Start the routing threads"); + + /* Later: TODO("Set the thresholds for the IN and OUT queues to create more routing threads as needed"); */ return ENOTSUP; }
--- a/include/freeDiameter/freeDiameter.h Thu Dec 03 17:36:35 2009 +0900 +++ b/include/freeDiameter/freeDiameter.h Fri Dec 04 17:23:06 2009 +0900 @@ -704,7 +704,6 @@ }; int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct); -int fd_app_find_common(struct fd_list * target, struct fd_list * reference); -int fd_app_gotcommon(struct fd_list * apps); +int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail); #endif /* _FREEDIAMETER_H */
--- a/include/freeDiameter/libfreeDiameter.h Thu Dec 03 17:36:35 2009 +0900 +++ b/include/freeDiameter/libfreeDiameter.h Fri Dec 04 17:23:06 2009 +0900 @@ -1343,6 +1343,7 @@ #define CC_DISCONNECT_PEER 282 /* AVPs (Vendor 0) */ +#define AC_USER_NAME 1 #define AC_PROXY_STATE 33 #define AC_HOST_IP_ADDRESS 257 #define AC_AUTH_APPLICATION_ID 258