Mercurial > hg > freeDiameter
changeset 43:2db15632a63d
Added a large part of connection establishment logic, to test
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Wed, 25 Nov 2009 19:07:09 +0900 |
parents | f4d94bc65e1f |
children | 8daaeae043c1 |
files | freeDiameter/CMakeLists.txt freeDiameter/apps.c freeDiameter/cnxctx.c freeDiameter/dict_base_proto.c freeDiameter/dispatch.c freeDiameter/endpoints.c freeDiameter/fD.h freeDiameter/p_ce.c freeDiameter/p_cnx.c freeDiameter/p_dw.c freeDiameter/p_psm.c include/freeDiameter/freeDiameter.h include/freeDiameter/libfreeDiameter.h libfreeDiameter/dictionary.c libfreeDiameter/fifo.c |
diffstat | 15 files changed, 944 insertions(+), 155 deletions(-) [+] |
line wrap: on
line diff
--- a/freeDiameter/CMakeLists.txt Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/CMakeLists.txt Wed Nov 25 19:07:09 2009 +0900 @@ -9,6 +9,7 @@ # List of source files SET(FD_COMMON_SRC fD.h + apps.c cnxctx.h config.c cnxctx.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freeDiameter/apps.c Wed Nov 25 19:07:09 2009 +0900 @@ -0,0 +1,91 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, 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 "fD.h" + +/* Merge information into a list of apps */ +int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct) +{ + struct fd_list * li; + int skip = 0; + + /* List is ordered by appid. Avoid duplicates */ + for (li = list; li->next != list; li = li->next) { + struct fd_app * na = (struct fd_app *)(li->next); + if (na->appid < aid) + continue; + + if (na->appid > aid) + break; + + /* Otherwise, we merge with existing entry -- ignore vendor id in this case */ + skip = 1; + + if (auth) + na->flags.auth = 1; + if (acct) + na->flags.acct = 1; + break; + } + + if (!skip) { + struct fd_app * new = NULL; + + CHECK_MALLOC( new = malloc(sizeof(struct fd_app)) ); + memset(new, 0, sizeof(struct fd_app)); + fd_list_init(&new->chain, NULL); + new->flags.auth = (auth ? 1 : 0); + new->flags.acct = (acct ? 1 : 0); + new->vndid = vid; + new->appid = aid; + fd_list_insert_after(li, &new->chain); + } + + 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) +{ + + +} + +/* Return true if at least one app was tagged common, false otherwise */ +int fd_app_gotcommon(struct fd_list * apps) +{ + +} +
--- a/freeDiameter/cnxctx.c Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/cnxctx.c Wed Nov 25 19:07:09 2009 +0900 @@ -996,7 +996,7 @@ CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming ); /* The magic function does it all */ - CHECK_FCT( fd_fifo_move( &conn->cc_incoming, alt_fifo, &conn->cc_alt ) ); + CHECK_FCT( fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ) ); return 0; } @@ -1113,6 +1113,9 @@ TRACE_ENTRY("%p", conn); CHECK_PARAMS_DO(conn, return); + + /* Avoid sending further events to the alt fifo */ + conn->cc_alt = NULL; /* In case of TLS, stop receiver thread, then close properly the gnutls session */ if ((conn->cc_tls) && (conn->cc_sctp_para.pairs > 1)) {
--- a/freeDiameter/dict_base_proto.c Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/dict_base_proto.c Wed Nov 25 19:07:09 2009 +0900 @@ -210,6 +210,9 @@ /* relay application */ { struct dict_application_data data = { 0xffffffff, "Relay" }; + #if AI_RELAY != 0xffffffff + #error "AI_RELAY definition mismatch" + #endif CHECK_dict_new( DICT_APPLICATION, &data , NULL, NULL); } } @@ -906,8 +909,8 @@ struct dict_object * type; struct dict_type_data tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Inband-Security-Id)" , NULL, NULL}; - struct dict_enumval_data t_0 = { "NO_INBAND_SECURITY", { .u32 = 0 }}; - struct dict_enumval_data t_1 = { "TLS", { .u32 = 1 }}; + struct dict_enumval_data t_0 = { "NO_INBAND_SECURITY", { .u32 = ACV_ISI_NO_INBAND_SECURITY }}; + struct dict_enumval_data t_1 = { "TLS", { .u32 = ACV_ISI_TLS }}; struct dict_avp_data data = { 299, /* Code */ #if AC_INBAND_SECURITY_ID != 299
--- a/freeDiameter/dispatch.c Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/dispatch.c Wed Nov 25 19:07:09 2009 +0900 @@ -35,12 +35,11 @@ #include "fD.h" +/* Add an application into the peer's supported apps */ int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct ) { + application_id_t aid = 0; vendor_id_t vid = 0; - application_id_t aid = 0; - struct fd_list * li; - int skip = 0; TRACE_ENTRY("%p %p %d %d", app, vendor, auth, acct); CHECK_PARAMS( app && (auth || acct) ); @@ -53,48 +52,15 @@ CHECK_FCT( fd_dict_getval(app, &data) ); aid = data.application_id; } - - - /* Now insert in the list ordered by appid. Avoid duplicates */ - for (li = &fd_g_config->cnf_apps; li->next != &fd_g_config->cnf_apps; li = li->next) { - struct fd_app * na = (struct fd_app *)(li->next); - if (na->appid < aid) - continue; - - if (na->appid > aid) - break; - - /* Otherwise, we merge with existing entry -- ignore vendor id in this case */ - skip = 1; - - if (auth) - na->flags.auth = 1; - if (acct) - na->flags.acct = 1; - break; + + if (vendor) { + enum dict_object_type type = 0; + struct dict_vendor_data data; + CHECK_FCT( fd_dict_gettype(vendor, &type) ); + CHECK_PARAMS( type == DICT_VENDOR ); + CHECK_FCT( fd_dict_getval(vendor, &data) ); + vid = data.vendor_id; } - if (!skip) { - struct fd_app * new = NULL; - - if (vendor) { - enum dict_object_type type = 0; - struct dict_vendor_data data; - CHECK_FCT( fd_dict_gettype(vendor, &type) ); - CHECK_PARAMS( type == DICT_VENDOR ); - CHECK_FCT( fd_dict_getval(vendor, &data) ); - vid = data.vendor_id; - } - - CHECK_MALLOC( new = malloc(sizeof(struct fd_app)) ); - memset(new, 0, sizeof(struct fd_app)); - fd_list_init(&new->chain, NULL); - new->flags.auth = (auth ? 1 : 0); - new->flags.acct = (acct ? 1 : 0); - new->vndid = vid; - new->appid = aid; - fd_list_insert_after(li, &new->chain); - } - - return 0; + return fd_app_merge(&fd_g_config->cnf_apps, aid, vid, auth, acct); }
--- a/freeDiameter/endpoints.c Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/endpoints.c Wed Nov 25 19:07:09 2009 +0900 @@ -125,6 +125,11 @@ for (li = list->next; li != list; li = li->next) { struct fd_endpoint * ep = (struct fd_endpoint *)li; ep->flags &= ~flags; + if (ep->flags == 0) { + li = li->prev; + fd_list_unlink(&ep->chain); + free(ep); + } } return 0;
--- a/freeDiameter/fD.h Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/fD.h Wed Nov 25 19:07:09 2009 +0900 @@ -66,6 +66,13 @@ #define DPR_TIMEOUT 15 /* in seconds */ #endif /* DPR_TIMEOUT */ +/* The Vendor-Id to advertise in CER/CEA */ +#ifndef MY_VENDOR_ID +#define MY_VENDOR_ID 0 /* Reserved value to tell it must be ignored */ +#endif /* MY_VENDOR_ID */ + + + /* Configuration */ int fd_conf_init(); void fd_conf_dump(); @@ -144,9 +151,10 @@ /* Data for transitional states before the peer is in OPEN state */ struct { - struct cnxctx * p_initiator; /* Connection before CEA is received */ struct cnxctx * p_receiver; /* Only used in case of election */ - pthread_t p_ini_thr; + struct msg * p_cer; /* Only used in case of election */ + + pthread_t p_ini_thr; /* Initiator thread for establishing a connection */ struct fd_list p_connparams; /* The list of connection attempts, see p_cnx.c */ }; @@ -264,9 +272,11 @@ int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer); int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid); int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator); -int fd_p_ce_winelection(struct fd_peer * peer); +int fd_p_ce_process_receiver(struct fd_peer * peer); +void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept); int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer); int fd_p_dw_timeout(struct fd_peer * peer); +int fd_p_dw_reopen(struct fd_peer * peer); int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer); int fd_p_dp_initiate(struct fd_peer * peer);
--- a/freeDiameter/p_ce.c Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/p_ce.c Wed Nov 25 19:07:09 2009 +0900 @@ -40,30 +40,590 @@ /* Save a connection as peer's principal */ static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx) { - TODO("Save *cnx into peer->p_cnxctx"); - TODO("Set fifo of *cnx to peer->p_events"); - TODO("If connection is already TLS, read the credentials"); - TODO("Read the remote endpoints"); + CHECK_PARAMS( peer->p_cnxctx == NULL ); + + /* Save the connection in peer */ + peer->p_cnxctx = *cnx; + *cnx = NULL; + + /* Set the events to be sent to the PSM */ + CHECK_FCT( fd_cnx_recv_setaltfifo(peer->p_cnxctx, peer->p_events) ); + + /* Read the credentials if possible */ + if (fd_cnx_getTLS(peer->p_cnxctx)) { + CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) ); + } + + /* Read the endpoints, maybe used to reconnect to the peer later */ + CHECK_FCT( fd_cnx_getendpoints(peer->p_cnxctx, NULL, &peer->p_hdr.info.pi_endpoints) ); + + /* Read the protocol */ + peer->p_hdr.info.runtime.pir_proto = fd_cnx_getproto(peer->p_cnxctx); + + return 0; +} + +/* Delete the peer connection, and cleanup associated information */ +void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept) +{ + peer->p_hdr.info.runtime.pir_cert_list = NULL; + peer->p_hdr.info.runtime.pir_cert_list_size = 0; + peer->p_hdr.info.runtime.pir_proto = 0; + + if (peer->p_cnxctx) { + if (cnx_kept != NULL) { + *cnx_kept = peer->p_cnxctx; + } else { + fd_cnx_destroy(peer->p_cnxctx); + } + peer->p_cnxctx = NULL; + } +} + +/* Election: compare the Diameter Ids, return true if the election is won */ +static __inline__ int election_result(struct fd_peer * peer) +{ + int ret = (strcasecmp(peer->p_hdr.info.pi_diamid, fd_g_config->cnf_diamid) < 0); + if (ret) { + TRACE_DEBUG(INFO, "Election WON against peer '%s'", peer->p_hdr.info.pi_diamid); + } else { + TRACE_DEBUG(INFO, "Election LOST against peer '%s'", peer->p_hdr.info.pi_diamid); + } + return ret; +} + +/* Add AVPs about local information in a CER or CEA */ +static int add_CE_info(struct msg *msg, struct cnxctx * cnx, int isi_tls, int isi_none) +{ + struct dict_object * dictobj = NULL; + struct avp * avp = NULL; + union avp_value val; + struct fd_list *li, local_ep = FD_LIST_INITIALIZER(local_ep); + + /* Add the Origin-* AVPs */ + CHECK_FCT( fd_msg_add_origin ( msg, 1 ) ); + + /* Find the model for Host-IP-Address AVP */ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &dictobj, ENOENT ) ); + + /* Get the list of endpoints */ + CHECK_FCT( fd_cnx_getendpoints(cnx, &local_ep, NULL) ); + + /* Add the AVP(s) -- not sure what is the purpose... We could probably only add the primary one ? */ + for (li = local_ep.next; li != &local_ep; li = li->next) { + struct fd_endpoint * ep = (struct fd_endpoint *)li; + + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_value_encode ( &ep->ss, avp ) ); + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + } + + + /* Vendor-Id, Product-Name, and Firmware-Revision AVPs */ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj, ENOENT ) ); + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); + val.u32 = MY_VENDOR_ID; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Product-Name", &dictobj, ENOENT ) ); + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); + val.os.data = (unsigned char *)FD_PROJECT_NAME; + val.os.len = strlen(FD_PROJECT_NAME); + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Firmware-Revision", &dictobj, ENOENT ) ); + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); + val.u32 = (uint32_t)(FD_PROJECT_VERSION_MAJOR * 10000 + FD_PROJECT_VERSION_MINOR * 100 + FD_PROJECT_VERSION_REV); + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + - return ENOTSUP; + /* Add the Inband-Security-Id AVP if needed */ + if (isi_tls || isi_none) { + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Inband-Security-Id", &dictobj, ENOENT ) ); + + if (isi_none) { + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); + val.u32 = ACV_ISI_NO_INBAND_SECURITY; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + } + + if (isi_tls) { + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); + val.u32 = ACV_ISI_TLS; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + } + } + + /* List of local applications */ + { + struct dict_object * dictobj_auth = NULL; + struct dict_object * dictobj_acct = NULL; + struct dict_object * dictobj_vid = NULL; + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id", &dictobj, ENOENT ) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj_vid, ENOENT ) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &dictobj_auth, ENOENT ) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &dictobj_acct, ENOENT ) ); + + for (li = fd_g_config->cnf_apps.next; li != &fd_g_config->cnf_apps; li = li->next) { + struct fd_app * a = (struct fd_app *)(li); + + if (a->flags.auth) { + CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) ); + val.u32 = a->appid; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + if (a->vndid != 0) { + struct avp * avp2 = NULL; + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) ); + CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) ); + avp = avp2; + CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) ); + val.u32 = a->vndid; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) ); + } + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + } + if (a->flags.acct) { + CHECK_FCT( fd_msg_avp_new ( dictobj_acct, 0, &avp ) ); + val.u32 = a->appid; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + if (a->vndid != 0) { + struct avp * avp2 = NULL; + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) ); + CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) ); + avp = avp2; + CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) ); + val.u32 = a->vndid; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) ); + } + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + } + } + + /* do not forget the relay application */ + if (! fd_g_config->cnf_flags.no_fwd) { + CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) ); + val.u32 = AI_RELAY; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + } + } + + /* Add the list of supported vendors */ + { + uint32_t * array = fd_dict_get_vendorid_list(fd_g_config->cnf_dict); + if (array) { + int i = 0; + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Supported-Vendor-Id", &dictobj, ENOENT ) ); + + while (array[i] != 0) { + CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); + val.u32 = array[i]; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + i++; + } + + free(array); + } + } + + return 0; +} + +/* Remove any information saved from a previous CER/CEA exchange */ +static void cleanup_remote_CE_info(struct fd_peer * peer) +{ + free(peer->p_hdr.info.runtime.pir_realm); + peer->p_hdr.info.runtime.pir_realm = NULL; + peer->p_hdr.info.runtime.pir_vendorid = 0; + peer->p_hdr.info.runtime.pir_orstate = 0; + free(peer->p_hdr.info.runtime.pir_prodname); + peer->p_hdr.info.runtime.pir_prodname = NULL; + peer->p_hdr.info.runtime.pir_firmrev = 0; + peer->p_hdr.info.runtime.pir_relay = 0; + peer->p_hdr.info.runtime.pir_isi = 0; + while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) { + struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next; + fd_list_unlink(li); + free(li); + } + + fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV /* Remove previously advertised endpoints */ ); } -static int process_valid_CEA(struct fd_peer * peer, struct msg ** cea) +/* Extract information sent by the remote peer and save it in our peer structure */ +static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, char ** error_code) { - /* Save info from the CEA into the peer */ + struct avp * avp = NULL; - /* Handshake if needed */ + cleanup_remote_CE_info(peer); + + CHECK_FCT( fd_msg_browse( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) ); - /* Save credentials if needed */ + /* Loop on all AVPs and save what we are interrested into */ + while (avp) { + struct avp_hdr * hdr; + + CHECK_FCT( fd_msg_avp_hdr( avp, &hdr ) ); + + if (hdr->avp_flags & AVP_FLAG_VENDOR) { + /* Ignore all vendor-specific AVPs in CER/CEA because we don't support any currently */ + TRACE_DEBUG(FULL, "Ignored a vendor AVP in CER / CEA"); + fd_msg_dump_one(FULL, avp); + goto next; + } + + switch (hdr->avp_code) { + case AC_ORIGIN_HOST: /* Origin-Host */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + /* We check that the value matches what we know, otherwise disconnect the peer */ + if (strncasecmp(hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, hdr->avp_value->os.len)) { + TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'\n", + hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid); + *error_code = "DIAMETER_UNKNOWN_PEER"; + return EINVAL; + } + + break; + + case AC_ORIGIN_REALM: /* Origin-Realm */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + /* In case of multiple AVPs */ + if (peer->p_hdr.info.runtime.pir_realm) { + TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-Realm AVP"); + goto next; + } + + /* Save the value -- we don't change the case to avoid risking breaking UTF-8 with poor tolower() impls. */ + CHECK_MALLOC( peer->p_hdr.info.runtime.pir_realm = calloc( hdr->avp_value->os.len + 1, 1 ) ); + memcpy(peer->p_hdr.info.runtime.pir_realm, hdr->avp_value->os.data, hdr->avp_value->os.len); + break; + + case AC_HOST_IP_ADDRESS: /* Host-IP-Address */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + { + sSS ss; + + /* Get the sockaddr value */ + memset(&ss, 0, sizeof(ss)); + CHECK_FCT( fd_msg_avp_value_interpret( avp, &ss) ); + + /* Save this endpoint in the list as advertized */ + CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, (sSA *)&ss, sizeof(sSS), EP_FL_ADV ) ); + } + break; + + case AC_VENDOR_ID: /* Vendor-Id */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + /* In case of multiple AVPs */ + if (peer->p_hdr.info.runtime.pir_vendorid) { + TRACE_DEBUG(INFO, "Ignored multiple instances of the Vendor-Id AVP"); + goto next; + } + + peer->p_hdr.info.runtime.pir_vendorid = hdr->avp_value->u32; + break; + + case AC_PRODUCT_NAME: /* Product-Name */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + /* In case of multiple AVPs */ + if (peer->p_hdr.info.runtime.pir_prodname) { + TRACE_DEBUG(INFO, "Ignored multiple instances of the Product-Name AVP"); + goto next; + } + + CHECK_MALLOC( peer->p_hdr.info.runtime.pir_prodname = calloc( hdr->avp_value->os.len + 1, 1 ) ); + memcpy(peer->p_hdr.info.runtime.pir_prodname, hdr->avp_value->os.data, hdr->avp_value->os.len); + break; + + case AC_ORIGIN_STATE_ID: /* Origin-State-Id */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + /* In case of multiple AVPs */ + if (peer->p_hdr.info.runtime.pir_orstate) { + TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-State-Id AVP"); + goto next; + } + + peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32; + break; + + case AC_SUPPORTED_VENDOR_ID: /* Supported-Vendor-Id */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + TRACE_DEBUG(FULL, "'%s' supports a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32); + break; + + case AC_VENDOR_SPECIFIC_APPLICATION_ID: /* Vendor-Specific-Application-Id (grouped)*/ + { + struct avp * inavp = NULL; + application_id_t aid = 0; + vendor_id_t vid = 0; + int auth = 0; + int acct = 0; + + /* get the first child AVP */ + CHECK_FCT( fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &inavp, NULL) ); + + while (inavp) { + struct avp_hdr * inhdr; + CHECK_FCT( fd_msg_avp_hdr( inavp, &inhdr ) ); + + if (inhdr->avp_flags & AVP_FLAG_VENDOR) { + TRACE_DEBUG(FULL, "Ignored a vendor AVP inside Vendor-Specific-Application-Id AVP"); + fd_msg_dump_one(FULL, avp); + goto innext; + } + + if (inhdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto innext; + } + switch (inhdr->avp_code) { + case AC_VENDOR_ID: /* Vendor-Id */ + vid = inhdr->avp_value->u32; + break; + case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */ + aid = inhdr->avp_value->u32; + auth += 1; + break; + case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */ + aid = inhdr->avp_value->u32; + acct += 1; + break; + /* ignore other AVPs */ + } + + innext: + /* Go to next in AVP */ + CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &inavp, NULL) ); + } + + if (auth + acct != 1) { + TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored"); + fd_msg_dump_one(FULL, avp); + } else { + /* Add an entry in the list */ + CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, aid, vid, auth, acct) ); + } + } + break; + + case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + if (hdr->avp_value->u32 == AI_RELAY) { + peer->p_hdr.info.runtime.pir_relay = 1; + } else { + CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 1, 0) ); + } + break; + + case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + if (hdr->avp_value->u32 == AI_RELAY) { + peer->p_hdr.info.runtime.pir_relay = 1; + } else { + /* Not clear if the relay application can be inside this AVP... */ + CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) ); + } + break; + + case AC_FIRMWARE_REVISION: /* Firmware-Revision */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + + peer->p_hdr.info.runtime.pir_firmrev = hdr->avp_value->u32; + break; + + case AC_INBAND_SECURITY_ID: /* Inband-Security-Id */ + if (hdr->avp_value == NULL) { + /* This is a sanity check */ + TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); + fd_msg_dump_one(NONE, avp); + ASSERT(0); /* To check if this really happens, and understand why... */ + goto next; + } + ASSERT( hdr->avp_value->u32 < 32 ); /* if false, we have to change the code bellow */ + peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32); + break; + } + +next: + /* Go to next AVP */ + CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); + } - TODO("..."); - return ENOTSUP; + return 0; +} + +/* Create a CER message for sending */ +static int create_CER(struct fd_peer * peer, struct cnxctx * cnx, struct msg ** cer) +{ + struct dict_object * dictobj = NULL; + int isi_tls = 0; + int isi_none = 0; + + /* Find CER dictionary object and create an instance */ + CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &dictobj, ENOENT ) ); + CHECK_FCT( fd_msg_new ( dictobj, MSGFL_ALLOC_ETEID, cer ) ); + + /* Do we need Inband-Security-Id AVPs ? */ + if (!fd_cnx_getTLS(cnx)) { + isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it event if the peer does not use the old mechanism */ + isi_tls = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD; + } + + /* Add the information about the local peer */ + CHECK_FCT( add_CE_info(*cer, cnx, isi_tls, isi_none) ); + + /* Done! */ + return 0; +} + + +/* Continue with the initiator side */ +static int to_waitcea(struct fd_peer * peer, struct cnxctx * cnx) +{ + /* We sent a CER on the connection, set the event queue so that we receive the CEA */ + CHECK_FCT( set_peer_cnx(peer, &cnx) ); + + /* Change state and reset the timer */ + CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCEA) ); + fd_psm_next_timeout(peer, 0, CEA_TIMEOUT); + return 0; +} + +/* Reject an incoming connection attempt */ +static void receiver_reject(struct cnxctx * recv_cnx, struct msg ** cer, char * rescode, char * errormsg) +{ + /* Create and send the CEA with appropriate error code */ + CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy ); + CHECK_FCT_DO( fd_msg_rescode_set(*cer, rescode, errormsg, NULL, 1 ), goto destroy ); + CHECK_FCT_DO( fd_out_send(cer, recv_cnx, NULL), goto destroy ); + + /* And now destroy this connection */ +destroy: + fd_cnx_destroy(recv_cnx); + if (*cer) { + fd_msg_free(*cer); + *cer = NULL; + } +} + +/* We have established a new connection to the remote peer, send CER and eventually process the election */ +int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator) +{ + struct msg * cer = NULL; + + /* Send CER on the new connection */ + CHECK_FCT( create_CER(peer, initiator, &cer) ); + CHECK_FCT( fd_out_send(&cer, initiator, peer) ); + + /* Are we doing an election ? */ + if (peer->p_hdr.info.runtime.pir_state == STATE_WAITCNXACK_ELEC) { + if (election_result(peer)) { + /* Close initiator connection */ + fd_cnx_destroy(initiator); + + /* Process with the receiver side */ + CHECK_FCT( fd_p_ce_process_receiver(peer) ); + + } else { + + /* Answer an ELECTION LOST to the receiver side */ + receiver_reject(peer->p_receiver, &peer->p_cer, "ELECTION_LOST", NULL); + peer->p_receiver = NULL; + CHECK_FCT( to_waitcea(peer, initiator) ); + } + } else { + /* No election (yet) */ + CHECK_FCT( to_waitcea(peer, initiator) ); + } + + return 0; } /* We have received a Capabilities Exchange message on the peer connection */ int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer) { + char * ec; TRACE_ENTRY("%p %p", msg, peer); CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) ); @@ -77,15 +637,14 @@ 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, "DIAMETER_COMMAND_UNSUPPORTED", "No CER in current state", NULL, 1 ) ); + CHECK_FCT( fd_msg_rescode_set(*msg, "DIAMETER_COMMAND_UNSUPPORTED", "No CER allowed in current state", NULL, 1 ) ); /* msg now contains an answer message to send back */ CHECK_FCT_DO( fd_out_send(msg, peer->p_cnxctx, peer), /* In case of error the message has already been dumped */ ); - } /* If the state is not WAITCEA, just discard the message */ - if ((req) || (peer->p_hdr.info.runtime.pir_state != STATE_WAITCEA)) { + if (req || (peer->p_hdr.info.runtime.pir_state != STATE_WAITCEA)) { if (*msg) { fd_log_debug("Received CER/CEA message while in state '%s', discarded.\n", STATE_STR(peer->p_hdr.info.runtime.pir_state)); fd_msg_dump_walk(NONE, *msg); @@ -96,10 +655,156 @@ return 0; } - /* Ok, now we can accept the CEA */ - TODO("process_valid_CEA"); + /* Save info from the CEA into the peer */ + CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &ec), goto cleanup ); + + /* Handshake if needed, start clear otherwise */ + if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { + int todo = peer->p_hdr.info.config.pic_flags.sec & peer->p_hdr.info.runtime.pir_isi ; + + if (todo == PI_SEC_NONE) { + /* Ok for clear connection */ + TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid); + CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) ); + } else { + + fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE); + CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL), + { + /* Handshake failed ... */ + fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid); + goto cleanup; + } ); + + /* Retrieve the credentials */ + CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) ); + } + } + + /* Move to next state */ + if (peer->p_flags.pf_cnx_pb) { + fd_psm_change_state(peer, STATE_REOPEN ); + CHECK_FCT( fd_p_dw_reopen(peer) ); + } else { + fd_psm_change_state(peer, STATE_OPEN ); + fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); + } + + return 0; + +cleanup: + fd_p_ce_clear_cnx(peer, NULL); + + /* Send the error to the peer */ + CHECK_FCT( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL) ); + + return 0; +} + +/* Handle the receiver side after winning an election (or timeout on initiator side) */ +int fd_p_ce_process_receiver(struct fd_peer * peer) +{ + char * ec = NULL; + struct msg * msg = NULL; + int isi = 0; + + TRACE_ENTRY("%p", peer); + + CHECK_FCT( set_peer_cnx(peer, &peer->p_receiver) ); + msg = peer->p_cer; + peer->p_cer = NULL; + + /* Parse the content of the received CER */ + CHECK_FCT_DO( save_remote_CE_info(msg, peer, &ec), goto error_abort ); + + /* Validate the peer if needed */ + if (peer->p_flags.pf_responder) { + int res = fd_peer_validate( peer ); + if (res < 0) { + ec = "DIAMETER_UNKNOWN_PEER"; + goto error_abort; + } + CHECK_FCT( res ); + } - return ENOTSUP; + /* Do we send ISI back ? */ + if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { + if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) + isi = PI_SEC_NONE; /* Maybe we should also look at peer->p_hdr.info.runtime.pir_isi here ? */ + else + isi = PI_SEC_TLS_OLD; + } + + /* Reply a CEA */ + CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) ); + CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 1 ) ); + CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) ); + CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer) ); + + /* Handshake if needed */ + if (isi & PI_SEC_TLS_OLD) { + fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE); + CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_SERVER, peer->p_hdr.info.config.pic_priority, NULL), + { + /* Handshake failed ... */ + fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid); + goto cleanup; + } ); + + /* Retrieve the credentials */ + CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) ); + + /* Call second validation callback if needed */ + if (peer->p_cb2) { + TRACE_DEBUG(FULL, "Calling second validation callback for %s", peer->p_hdr.info.pi_diamid); + CHECK_FCT_DO( (*peer->p_cb2)( &peer->p_hdr.info ), + { + TRACE_DEBUG(INFO, "Validation callback rejected the peer %s after handshake", peer->p_hdr.info.pi_diamid); + CHECK_FCT( fd_psm_terminate( peer ) ); + return 0; + } ); + } + + } else { + if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { + TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid); + CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) ); + } + } + + /* Move to OPEN or REOPEN state */ + if (peer->p_flags.pf_cnx_pb) { + fd_psm_change_state(peer, STATE_REOPEN ); + CHECK_FCT( fd_p_dw_reopen(peer) ); + } else { + fd_psm_change_state(peer, STATE_OPEN ); + fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); + } + + return 0; + +error_abort: + if (ec) { + /* Create the error message */ + 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, ec, NULL, NULL, 1 ) ); + + /* msg now contains an answer message to send back */ + CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer), /* In case of error the message has already been dumped */ ); + } + +cleanup: + if (msg) { + fd_msg_free(msg); + } + fd_p_ce_clear_cnx(peer, NULL); + + /* Send the error to the peer */ + CHECK_FCT( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL) ); + + return 0; } /* We have received a CER on a new connection for this peer */ @@ -107,60 +812,47 @@ { switch (peer->p_hdr.info.runtime.pir_state) { case STATE_CLOSED: - TODO("Handle the CER, validate the peer if needed (and set expiry), set the alt_fifo in the connection, reply a CEA, eventually handshake (OPEN_HANDSHAKE), move to OPEN or REOPEN state"); - /* In case of error : DIAMETER_UNKNOWN_PEER */ + peer->p_receiver = *cnx; + *cnx = NULL; + peer->p_cer = *msg; + *msg = NULL; + CHECK_FCT( fd_p_ce_process_receiver(peer) ); break; case STATE_WAITCNXACK: - TODO("Save the parameters in the peer, move to STATE_WAITCNXACK_ELEC"); + /* Save the parameters in the peer, move to STATE_WAITCNXACK_ELEC */ + peer->p_receiver = *cnx; + *cnx = NULL; + peer->p_cer = *msg; + *msg = NULL; + CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCNXACK_ELEC) ); break; case STATE_WAITCEA: - TODO("Election"); + if (election_result(peer)) { + + /* Close initiator connection (was already set as principal) */ + fd_p_ce_clear_cnx(peer, NULL); + + /* and go on with the receiver side */ + peer->p_receiver = *cnx; + *cnx = NULL; + peer->p_cer = *msg; + *msg = NULL; + CHECK_FCT( fd_p_ce_process_receiver(peer) ); + + } else { + + /* Answer an ELECTION LOST to the receiver side and continue */ + receiver_reject(*cnx, msg, "ELECTION_LOST", "Please answer my CER instead, you won the election."); + *cnx = NULL; + } break; default: - TODO("Reply with error CEA"); - TODO("Close the connection"); - /* reject_incoming_connection */ - + receiver_reject(*cnx, msg, "DIAMETER_UNABLE_TO_COMPLY", "Invalid state to receive a new connection attempt"); + *cnx = NULL; } - - return ENOTSUP; -} - -static int do_election(struct fd_peer * peer) -{ - TODO("Compare diameter Ids"); - - /* ELECTION_LOST error code ?*/ - - TODO("If we lost the election, we close the received connection and go to WAITCEA"); - TODO("If we won the election, we close initiator cnx, then call fd_p_ce_winelection"); - - + return 0; } - -/* We have established a new connection to the remote peer, send CER and eventually do election */ -int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator) -{ - /* if not election */ - TODO("Set the connection as peer's (setaltfifo)"); - TODO("Send CER"); - TODO("Move to WAITCEA"); - TODO("Change timer to CEA_TIMEOUT"); - - /* if election */ - TODO("Send CER on cnx"); - TODO("Do the election"); - - return ENOTSUP; -} - -/* Handle the receiver side after winning an election (or timeout on initiator side) */ -int fd_p_ce_winelection(struct fd_peer * peer) -{ - TODO("Move the receiver side connection to peer principal, set the altfifo"); - TODO("Then handle the received CER"); -}
--- a/freeDiameter/p_cnx.c Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/p_cnx.c Wed Nov 25 19:07:09 2009 +0900 @@ -245,6 +245,9 @@ fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF); return NULL; } ); + } else { + /* Prepare to receive the next message */ + CHECK_FCT_DO( fd_cnx_start_clear(cnx, 0), goto fatal_error ); } /* Upon success, generate FDEVP_CNX_ESTABLISHED */
--- a/freeDiameter/p_dw.c Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/p_dw.c Wed Nov 25 19:07:09 2009 +0900 @@ -53,4 +53,13 @@ return ENOTSUP; } - + +/* Handle DW exchanges after the peer has come alive again */ +int fd_p_dw_reopen(struct fd_peer * peer) +{ + TODO("..."); + + return ENOTSUP; +} + +
--- a/freeDiameter/p_psm.c Thu Nov 19 17:51:23 2009 +0900 +++ b/freeDiameter/p_psm.c Wed Nov 25 19:07:09 2009 +0900 @@ -249,15 +249,7 @@ fd_p_cnx_abort(peer, terminate); - if (peer->p_cnxctx) { - fd_cnx_destroy(peer->p_cnxctx); - peer->p_cnxctx = NULL; - } - - if (peer->p_initiator) { - fd_cnx_destroy(peer->p_initiator); - peer->p_initiator = NULL; - } + fd_p_ce_clear_cnx(peer, NULL); if (peer->p_receiver) { fd_cnx_destroy(peer->p_receiver); @@ -593,7 +585,7 @@ if (event == FDEVP_CNX_ESTABLISHED) { struct cnxctx * cnx = ev_data; - /* Release the resources of the thread */ + /* Release the resources of the connecting thread */ CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */); peer->p_ini_thr = (pthread_t)NULL; @@ -641,7 +633,7 @@ fd_p_cnx_abort(peer, 0); /* Handle receiver side */ - CHECK_FCT_DO( fd_p_ce_winelection(peer), goto psm_end ); + CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end ); break; } }
--- a/include/freeDiameter/freeDiameter.h Thu Nov 19 17:51:23 2009 +0900 +++ b/include/freeDiameter/freeDiameter.h Wed Nov 25 19:07:09 2009 +0900 @@ -335,6 +335,7 @@ uint32_t pir_firmrev; /* Content of the Firmware-Revision AVP */ int pir_relay; /* The remote peer advertized the relay application */ struct fd_list pir_apps; /* applications advertised by the remote peer, except relay (pi_flags.relay) */ + int pir_isi; /* Inband-Security-Id advertised (PI_SEC_* bits) */ int pir_proto; /* The L4 protocol currently used with the peer (IPPROTO_TCP or IPPROTO_SCTP) */ const gnutls_datum_t *pir_cert_list; /* The (valid) credentials that the peer has presented, or NULL if TLS is not used */ @@ -422,14 +423,15 @@ * This callback is called when a new connection is being established from an unknown peer, * after the CER is received. An extension must register such callback with peer_validate_register. * - * If (info->pi_flags.sec == PI_SEC_TLS_OLD) the extension may instruct the daemon explicitely - * to not use TLS by clearing info->pi_flags.inband_tls -- only if inband_none is set. + * The callback can learn if the peer has sent Inband-Security-Id AVPs in runtime.pir_isi fields. + * It can also learn if a handshake has already been performed in runtime.pir_cert_list field. + * The callback must set the value of config.pic_flags.sec appropriately to allow a connection without TLS. * - * If (info->pi_flags.sec == PI_SEC_TLS_OLD) and info->pi_flags.inband_tls is set, + * If the old TLS mechanism is used, * the extension may also need to check the credentials provided during the TLS * exchange (remote certificate). For this purpose, it may set the address of a new callback * to be called once the handshake is completed. This new callback receives the information - * structure as parameter (with pi_sec_data set) and returns 0 if the credentials are correct, + * structure as parameter (with pir_cert_list set) and returns 0 if the credentials are correct, * or an error code otherwise. If the error code is received, the connection is closed and the * peer is destroyed. * @@ -544,4 +546,12 @@ void fd_ep_dump_one( char * prefix, struct fd_endpoint * ep, char * suffix ); void fd_ep_dump( int indent, struct fd_list * eps ); +/***************************************/ +/* Applications lists helpers */ +/***************************************/ + +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); + #endif /* _FREEDIAMETER_H */
--- a/include/freeDiameter/libfreeDiameter.h Thu Nov 19 17:51:23 2009 +0900 +++ b/include/freeDiameter/libfreeDiameter.h Wed Nov 25 19:07:09 2009 +0900 @@ -717,8 +717,10 @@ printf("my vendor id: %d\n", myvendordata.vendor_id ); } - */ + +/* Special function: */ +uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict); /* *************************************************************************** @@ -1328,6 +1330,9 @@ */ /* Define some hard-coded values */ +/* Application */ +#define AI_RELAY 0xffffffff + /* Commands Codes */ #define CC_CAPABILITIES_EXCHANGE 257 #define CC_RE_AUTH 258 @@ -1368,6 +1373,8 @@ #define AC_ERROR_REPORTING_HOST 294 #define AC_ORIGIN_REALM 296 #define AC_INBAND_SECURITY_ID 299 +#define ACV_ISI_NO_INBAND_SECURITY 0 +#define ACV_ISI_TLS 1 /* Error codes */ #define ER_DIAMETER_SUCCESS 2001 @@ -2425,18 +2432,18 @@ * FUNCTION: fd_fifo_move * * PARAMETERS: - * old : Location of a FIFO that is to be emptied and deleted. + * old : Location of a FIFO that is to be emptied. * new : A FIFO that will receive the old data. * loc_update : if non NULL, a place to store the pointer to new FIFO atomically with the move. * * DESCRIPTION: - * Delete a queue and move its content to another one atomically. + * Empties a queue and move its content to another one atomically. * * RETURN VALUE: * 0 : The queue has been destroyed successfully. * EINVAL : A parameter is invalid. */ -int fd_fifo_move ( struct fifo ** old, struct fifo * new, struct fifo ** loc_update ); +int fd_fifo_move ( struct fifo * old, struct fifo * new, struct fifo ** loc_update ); /* * FUNCTION: fd_fifo_length
--- a/libfreeDiameter/dictionary.c Thu Nov 19 17:51:23 2009 +0900 +++ b/libfreeDiameter/dictionary.c Wed Nov 25 19:07:09 2009 +0900 @@ -1528,7 +1528,7 @@ new->dict_vendors.list[0].o = NULL; /* overwrite since element is also sentinel for this list. */ - /* Initialize the sentinel for applciations */ + /* Initialize the sentinel for applications */ init_object( &new->dict_applications, DICT_APPLICATION ); new->dict_applications.data.application.application_name = "Diameter Common Messages"; new->dict_applications.list[0].o = NULL; /* overwrite since since element is also sentinel for this list. */
--- a/libfreeDiameter/fifo.c Thu Nov 19 17:51:23 2009 +0900 +++ b/libfreeDiameter/fifo.c Wed Nov 25 19:07:09 2009 +0900 @@ -182,55 +182,52 @@ return 0; } -/* Move the content of old into new, and update loc_update atomically */ -int fd_fifo_move ( struct fifo ** old, struct fifo * new, struct fifo ** loc_update ) +/* Move the content of old into new, and update loc_update atomically. We leave the old queue empty but valid */ +int fd_fifo_move ( struct fifo * old, struct fifo * new, struct fifo ** loc_update ) { struct fifo * q; int loops = 0; TRACE_ENTRY("%p %p %p", old, new, loc_update); - CHECK_PARAMS( old && CHECK_FIFO( *old ) && CHECK_FIFO( new )); + CHECK_PARAMS( CHECK_FIFO( old ) && CHECK_FIFO( new )); - q = *old; - CHECK_PARAMS( ! q->data ); + CHECK_PARAMS( ! old->data ); if (new->high) { TODO("Implement support for thresholds in fd_fifo_move..."); } /* Update loc_update */ - *old = NULL; if (loc_update) *loc_update = new; /* Lock the queues */ - CHECK_POSIX( pthread_mutex_lock( &q->mtx ) ); + CHECK_POSIX( pthread_mutex_lock( &old->mtx ) ); CHECK_POSIX( pthread_mutex_lock( &new->mtx ) ); /* Any waiting thread on the old queue returns an error */ - q->eyec = 0xdead; - while (q->thrs) { - CHECK_POSIX( pthread_cond_signal(&q->cond) ); - CHECK_POSIX( pthread_mutex_unlock( &q->mtx )); + old->eyec = 0xdead; + while (old->thrs) { + CHECK_POSIX( pthread_cond_signal(&old->cond) ); + CHECK_POSIX( pthread_mutex_unlock( &old->mtx )); pthread_yield(); - CHECK_POSIX( pthread_mutex_lock( &q->mtx ) ); + CHECK_POSIX( pthread_mutex_lock( &old->mtx ) ); ASSERT( ++loops < 10 ); /* detect infinite loops */ } /* Move all data from old to new */ - fd_list_move_end( &new->list, &q->list ); - if (q->count && (!new->count)) { + fd_list_move_end( &new->list, &old->list ); + if (old->count && (!new->count)) { CHECK_POSIX( pthread_cond_signal(&new->cond) ); } - new->count += q->count; + new->count += old->count; - /* Destroy old */ - CHECK_POSIX( pthread_mutex_unlock( &q->mtx ) ); - CHECK_POSIX( pthread_cond_destroy( &q->cond ) ); - CHECK_POSIX( pthread_mutex_destroy( &q->mtx ) ); - free(q); + /* Reset old */ + old->count = 0; + old->eyec = FIFO_EYEC; - /* Unlock new, we're done */ + /* Unlock, we're done */ CHECK_POSIX( pthread_mutex_unlock( &new->mtx ) ); + CHECK_POSIX( pthread_mutex_unlock( &old->mtx ) ); return 0; }