This commit is contained in:
zhangsz committed 2025-03-03 11:01:26 +08:00
1 parent 5f1710dc22
commit dae6fc93f7
1057 files changed
+519829

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* 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 "fdcore-internal.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;
}
/* 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;
}
/* Check if two lists have at least one common application */
int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found)
{
struct fd_list * li1, *li2;
TRACE_ENTRY("%p %p %p", list1, list2, common_found);
CHECK_PARAMS( list1 && list2 && common_found );
/* Both lists are ordered, so advance both pointers at the same time */
for (li1 = list1->next, li2 = list2->next; (li1 != list1) && (li2 != list2); ) {
struct fd_app * a1 = (struct fd_app *)li1, *a2 = (struct fd_app *)li2;
if (a1->appid < a2->appid) {
li1 = li1->next;
continue;
}
if (a1->appid > a2->appid) {
li2 = li2->next;
continue;
}
/* They are equal, compare the applications */
if ((a1->flags.auth && a2->flags.auth) || (a1->flags.acct && a2->flags.acct)) {
/* found! */
*common_found = 1;
return 0;
}
/* This application is not common, advance both lists */
li1 = li1->next;
li2 = li2->next;
}
/* We did not find a common app */
*common_found = 0;
return 0;
}
/* Remove the apps from a list */
int fd_app_empty(struct fd_list * list)
{
TRACE_ENTRY("%p", list);
CHECK_PARAMS( list );
while (!FD_IS_LIST_EMPTY(list)) {
struct fd_list * li = list->next;
fd_list_unlink(li);
free(li);
}
return 0;
}
File diff suppressed because it is too large. Load diff
+153
View File
@@ -0,0 +1,153 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
/* This file contains the definitions for internal use in the connection context files */
#ifndef _CNXCTX_H
#define _CNXCTX_H
/* Maximum time we allow a connection to be blocked because of head-of-the-line buffers. After this delay, connection is considered in error. */
#define MAX_HOTL_BLOCKING_TIME 1000 /* ms */
/* The connection context structure */
struct cnxctx {
char cc_id[60]; /* The name of this connection. the first 5 chars are reserved for flags display (cc_state). */
char cc_remid[60]; /* Id of remote peer */
int cc_socket; /* The socket object of the connection -- <=0 if no socket is created */
int cc_family; /* AF_INET or AF_INET6 (mixed) */
int cc_proto; /* IPPROTO_TCP or IPPROTO_SCTP */
uint32_t cc_state; /* True if the object is being destroyed: we don't send events anymore. access with fd_cnx_getstate() */
#define CC_STATUS_CLOSING 1
#define CC_STATUS_ERROR 2
#define CC_STATUS_SIGNALED 4
#define CC_STATUS_TLS 8
pthread_t cc_rcvthr; /* thread for receiving messages on the connection */
int cc_loop; /* tell the thread if it loops or stops after the first message is received */
struct fifo * cc_incoming; /* FIFO queue of events received on the connection, FDEVP_CNX_* */
struct fifo * cc_alt; /* alternate fifo to send FDEVP_CNX_* events to. */
/* If cc_tls == true */
struct {
DiamId_t cn; /* If not NULL, remote certif will be checked to match this Common Name */
int mode; /* GNUTLS_CLIENT / GNUTLS_SERVER */
int algo; /* ALGO_HANDSHAKE_DEFAULT / ALGO_HANDSHAKE_3436 */
gnutls_session_t session; /* Session object (stream #0 in case of SCTP) */
} cc_tls_para;
/* If cc_proto == SCTP */
struct {
uint16_t str_out; /* Out streams */
uint16_t str_in; /* In streams */
uint16_t pairs; /* max number of pairs ( = min(in, out)) */
uint16_t next; /* # of stream the next message will be sent to */
int unordered; /* boolean telling if use of streams > 0 is permitted */
} cc_sctp_para;
/* If both conditions */
struct {
struct sctp3436_ctx *array; /* an array of cc_sctp_para.pairs elements -- the #0 is special (session is outside)*/
struct sr_store *sess_store; /* Session data of the master session, to resume the children sessions */
} cc_sctp3436_data;
};
void fd_cnx_markerror(struct cnxctx * conn);
uint32_t fd_cnx_getstate(struct cnxctx * conn);
int fd_cnx_teststate(struct cnxctx * conn, uint32_t flag);
void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate);
void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate);
struct fifo * fd_cnx_target_queue(struct cnxctx * conn);
/* Socket */
ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length);
void fd_cnx_s_setto(int sock);
/* TLS */
int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session);
int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priority, void * alt_creds);
#ifndef GNUTLS_VERSION_300
int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose);
#endif /* GNUTLS_VERSION_300 */
/* TCP */
int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
int fd_tcp_listen( int sock );
int fd_tcp_client( int *sock, sSA * sa, socklen_t salen );
int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl);
int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl);
#ifndef DISABLE_SCTP
/* SCTP */
int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port );
int fd_sctp_listen( int sock );
int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list );
int fd_sctp_get_local_ep(int sock, struct fd_list * list);
int fd_sctp_get_remote_ep(int sock, struct fd_list * list);
int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary );
ssize_t fd_sctp_sendstrv(struct cnxctx * conn, uint16_t strid, const struct iovec *iov, int iovcnt);
int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event);
/* TLS over SCTP (multi-stream) */
struct sctp3436_ctx {
struct cnxctx *parent; /* for info such as socket, conn name, event list */
uint16_t strid; /* Stream # of this session */
struct fifo *raw_recv; /* Raw data received on this stream, for demux */
struct {
uint8_t *buf;
size_t bufsz;
size_t offset;
} partial; /* If the pull function did not read the full content of first message in raw, it stores it here for next read call. */
pthread_t thr; /* Thread to decrypt raw data in this pair of streams */
gnutls_session_t session; /* TLS context using this pair of streams -- except if strid == 0, in that case session is outside the array */
};
int fd_sctp3436_init(struct cnxctx * conn);
int fd_sctp3436_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds);
int fd_sctp3436_startthreads(struct cnxctx * conn, int others);
void fd_sctp3436_bye(struct cnxctx * conn);
void fd_sctp3436_waitthreadsterm(struct cnxctx * conn);
void fd_sctp3436_gnutls_deinit_others(struct cnxctx * conn);
void fd_sctp3436_stopthreads(struct cnxctx * conn);
void fd_sctp3436_destroy(struct cnxctx * conn);
#endif /* DISABLE_SCTP */
#endif /* _CNXCTX_H */
+643
View File
@@ -0,0 +1,643 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2015, 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 "fdcore-internal.h"
#include <sys/stat.h>
/* Configuration management */
#ifndef GNUTLS_DEFAULT_PRIORITY
# define GNUTLS_DEFAULT_PRIORITY "NORMAL"
#endif /* GNUTLS_DEFAULT_PRIORITY */
#ifndef GNUTLS_DEFAULT_DHBITS
# define GNUTLS_DEFAULT_DHBITS 1024
#endif /* GNUTLS_DEFAULT_DHBITS */
/* Initialize the fd_g_config structure to default values -- it should already have been initialized to all-0 */
int fd_conf_init()
{
TRACE_ENTRY();
fd_g_config->cnf_eyec = EYEC_CONFIG;
fd_g_config->cnf_timer_tc = 30;
fd_g_config->cnf_timer_tw = 30;
fd_g_config->cnf_port = DIAMETER_PORT;
fd_g_config->cnf_port_tls = DIAMETER_SECURE_PORT;
fd_g_config->cnf_sctp_str = 30;
fd_g_config->cnf_thr_srv = 10;
fd_g_config->cnf_dispthr = 1;// = 10;
fd_list_init(&fd_g_config->cnf_endpoints, NULL);
fd_list_init(&fd_g_config->cnf_apps, NULL);
#ifdef DISABLE_SCTP
fd_g_config->cnf_flags.no_sctp = 1;
#endif /* DISABLE_SCTP */
fd_g_config->cnf_orstateid = (uint32_t) time(NULL);
CHECK_FCT( fd_dict_init(&fd_g_config->cnf_dict) );
CHECK_FCT( fd_fifo_new(&fd_g_config->cnf_main_ev, 0) );
/* TLS parameters */
CHECK_GNUTLS_DO( gnutls_certificate_allocate_credentials (&fd_g_config->cnf_sec_data.credentials), return ENOMEM );
CHECK_GNUTLS_DO( gnutls_dh_params_init (&fd_g_config->cnf_sec_data.dh_cache), return ENOMEM );
#ifdef GNUTLS_VERSION_300
CHECK_GNUTLS_DO( gnutls_x509_trust_list_init(&fd_g_config->cnf_sec_data.trustlist, 0), return ENOMEM );
#endif /* GNUTLS_VERSION_300 */
return 0;
}
DECLARE_FD_DUMP_PROTOTYPE(fd_conf_dump)
{
FD_DUMP_HANDLE_OFFSET();
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "freeDiameter configuration:\n"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Default trace level .... : %+d\n", fd_g_debug_lvl), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Configuration file ..... : %s\n", fd_g_config->cnf_file), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Diameter Identity ...... : %s (l:%zi)\n", fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Diameter Realm ......... : %s (l:%zi)\n", fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Tc Timer ............... : %u\n", fd_g_config->cnf_timer_tc), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Tw Timer ............... : %u\n", fd_g_config->cnf_timer_tw), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local port ............. : %hu\n", fd_g_config->cnf_port), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local secure port ...... : %hu\n", fd_g_config->cnf_port_tls), return NULL);
if (fd_g_config->cnf_port_3436) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local SCTP TLS port .... : %hu\n", fd_g_config->cnf_port_3436), return NULL);
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of SCTP streams . : %hu\n", fd_g_config->cnf_sctp_str), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of clients thr .. : %d\n", fd_g_config->cnf_thr_srv), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of app threads .. : %hu\n", fd_g_config->cnf_dispthr), return NULL);
if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local endpoints ........ : Default (use all available)\n"), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local endpoints ........ : "), return NULL);
CHECK_MALLOC_DO( fd_ep_dump( FD_DUMP_STD_PARAMS, 0, 0, &fd_g_config->cnf_endpoints ), return NULL);
}
if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_apps)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local applications ..... : (none)"), return NULL);
} else {
struct fd_list * li = fd_g_config->cnf_apps.next;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local applications ..... : "), return NULL);
while (li != &fd_g_config->cnf_apps) {
struct fd_app * app = (struct fd_app *)li;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "App: %u,%s%s,Vnd:%u\t",
app->appid,
app->flags.auth ? "Au" : "--",
app->flags.acct ? "Ac" : "--",
app->vndid), return NULL);
li = li->next;
}
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n Flags : - IP ........... : %s\n", fd_g_config->cnf_flags.no_ip4 ? "DISABLED" : "Enabled"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - IPv6 ......... : %s\n", fd_g_config->cnf_flags.no_ip6 ? "DISABLED" : "Enabled"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Relay app .... : %s\n", fd_g_config->cnf_flags.no_fwd ? "DISABLED" : "Enabled"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - TCP .......... : %s\n", fd_g_config->cnf_flags.no_tcp ? "DISABLED" : "Enabled"), return NULL);
#ifdef DISABLE_SCTP
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - SCTP ......... : DISABLED (at compilation)\n"), return NULL);
#else /* DISABLE_SCTP */
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - SCTP ......... : %s\n", fd_g_config->cnf_flags.no_sctp ? "DISABLED" : "Enabled"), return NULL);
#endif /* DISABLE_SCTP */
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Pref. proto .. : %s\n", fd_g_config->cnf_flags.pr_tcp ? "TCP" : "SCTP"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - TLS method ... : %s\n", fd_g_config->cnf_flags.tls_alg ? "INBAND" : "Separate port"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " TLS : - Certificate .. : %s\n", fd_g_config->cnf_sec_data.cert_file ?: "(NONE)"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Private key .. : %s\n", fd_g_config->cnf_sec_data.key_file ?: "(NONE)"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - CA (trust) ... : %s (%d certs)\n", fd_g_config->cnf_sec_data.ca_file ?: "(none)", fd_g_config->cnf_sec_data.ca_file_nr), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - CRL .......... : %s\n", fd_g_config->cnf_sec_data.crl_file ?: "(none)"), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Priority ..... : %s\n", fd_g_config->cnf_sec_data.prio_string ?: "(default: '" GNUTLS_DEFAULT_PRIORITY "')"), return NULL);
if (fd_g_config->cnf_sec_data.dh_file) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - DH file ...... : %s\n", fd_g_config->cnf_sec_data.dh_file), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - DH bits ...... : %d\n", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS), return NULL);
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Origin-State-Id ........ : %u", fd_g_config->cnf_orstateid), return NULL);
return *buf;
}
/* read contents of a file opened in "rb" mode and alloc this data into a gnutls_datum_t (must be freed afterwards) */
int fd_conf_stream_to_gnutls_datum(FILE * pemfile, gnutls_datum_t *out)
{
size_t alloc = 0;
CHECK_PARAMS( pemfile && out );
memset(out, 0, sizeof(gnutls_datum_t));
do {
uint8_t * realloced = NULL;
size_t read = 0;
if (alloc < out->size + BUFSIZ + 1) {
alloc += alloc / 2 + BUFSIZ + 1;
CHECK_MALLOC_DO( realloced = realloc(out->data, alloc),
{
free(out->data);
return ENOMEM;
} )
out->data = realloced;
}
read = fread( out->data + out->size, 1, alloc - out->size - 1, pemfile );
out->size += read;
if (ferror(pemfile)) {
int err = errno;
TRACE_DEBUG(INFO, "An error occurred while reading file: %s", strerror(err));
return err;
}
} while (!feof(pemfile));
out->data[out->size] = '\0';
return 0;
}
#ifdef GNUTLS_VERSION_300
/* inspired from GnuTLS manual */
static int fd_conf_print_details_func (gnutls_x509_crt_t cert,
gnutls_x509_crt_t issuer, gnutls_x509_crl_t crl,
unsigned int verification_output)
{
char name[512];
char issuer_name[512];
size_t name_size;
size_t issuer_name_size;
if (!TRACE_BOOL(GNUTLS_DBG_LEVEL))
return 0;
issuer_name_size = sizeof (issuer_name);
gnutls_x509_crt_get_issuer_dn (cert, issuer_name, &issuer_name_size);
name_size = sizeof (name);
gnutls_x509_crt_get_dn (cert, name, &name_size);
fd_log_debug("\tSubject: %s", name);
fd_log_debug("\tIssuer: %s", issuer_name);
if (issuer != NULL)
{
issuer_name_size = sizeof (issuer_name);
gnutls_x509_crt_get_dn (issuer, issuer_name, &issuer_name_size);
fd_log_debug("\tVerified against: %s", issuer_name);
}
if (crl != NULL)
{
issuer_name_size = sizeof (issuer_name);
gnutls_x509_crl_get_issuer_dn (crl, issuer_name, &issuer_name_size);
fd_log_debug("\tVerified against CRL of: %s", issuer_name);
}
fd_log_debug("\tVerification output: %x", verification_output);
return 0;
}
#endif /* GNUTLS_VERSION_300 */
#ifndef GNUTLS_VERSION_300
GCC_DIAG_OFF("-Wdeprecated-declarations")
#endif /* !GNUTLS_VERSION_300 */
/* Parse the configuration file (using the yacc parser) */
int fd_conf_parse()
{
FILE * fddin;
const char * orig = NULL;
/* Attempt to find the configuration file */
if (!fd_g_config->cnf_file)
fd_g_config->cnf_file = FD_DEFAULT_CONF_FILENAME;
fddin = fopen(fd_g_config->cnf_file, "r");
if ((fddin == NULL) && (*fd_g_config->cnf_file != '/')) {
char * new_cnf = NULL;
/* We got a relative path, attempt to add the default directory prefix */
orig = fd_g_config->cnf_file;
CHECK_MALLOC( new_cnf = malloc(strlen(orig) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */
sprintf( new_cnf, DEFAULT_CONF_PATH "/%s", orig );
fd_g_config->cnf_file = new_cnf;
fddin = fopen(fd_g_config->cnf_file, "r");
}
if (fddin == NULL) {
int ret = errno;
LOG_F("Unable to open configuration file for reading; tried the following locations: %s%s%s; Error: %s",
orig ?: "", orig? " and " : "", fd_g_config->cnf_file, strerror(ret));
return ret;
}
/* call yacc parser */
TRACE_DEBUG (FULL, "Parsing configuration file: %s", fd_g_config->cnf_file);
CHECK_FCT( fddparse(fd_g_config) );
/* close the file */
fclose(fddin);
/* Check that TLS private key was given */
if (! fd_g_config->cnf_sec_data.key_file) {
/* If TLS is not enabled, we allow empty TLS configuration */
if ((fd_g_config->cnf_port_tls == 0) && (fd_g_config->cnf_flags.tls_alg == 0)) {
LOG_N("TLS is disabled, this is *NOT* a recommended practice! Diameter protocol conveys highly sensitive information on your users.");
fd_g_config->cnf_sec_data.tls_disabled = 1;
} else {
LOG_F( "Missing private key configuration for TLS. Please provide the TLS_cred configuration directive.");
return EINVAL;
}
}
/* Resolve hostname if not provided */
if (fd_g_config->cnf_diamid == NULL) {
char buf[HOST_NAME_MAX + 1];
struct addrinfo hints, *info;
int ret;
/* local host name */
CHECK_SYS(gethostname(buf, sizeof(buf)));
/* get FQDN */
memset(&hints, 0, sizeof hints);
hints.ai_flags = AI_CANONNAME;
ret = getaddrinfo(buf, NULL, &hints, &info);
if (ret != 0) {
TRACE_ERROR( "Error resolving local FQDN : '%s' : %s"
". Please provide Identity in configuration file.",
buf, gai_strerror(ret));
return EINVAL;
}
fd_g_config->cnf_diamid = info->ai_canonname;
CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 1) );
freeaddrinfo(info);
} else {
CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 0) );
}
/* Handle the realm part */
if (fd_g_config->cnf_diamrlm == NULL) {
char * start = NULL;
/* Check the diameter identity is a fqdn */
start = strchr(fd_g_config->cnf_diamid, '.');
if ((start == NULL) || (start[1] == '\0')) {
TRACE_ERROR( "Unable to extract realm from the Identity '%s'."
" Please fix your Identity setting or provide Realm.",
fd_g_config->cnf_diamid);
return EINVAL;
}
fd_g_config->cnf_diamrlm = start + 1;
CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 1) );
} else {
CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 0) );
}
/* Validate some flags */
if (fd_g_config->cnf_flags.no_ip4 && fd_g_config->cnf_flags.no_ip6) {
TRACE_ERROR( "IP and IPv6 cannot be disabled at the same time.");
return EINVAL;
}
if (fd_g_config->cnf_flags.no_tcp && fd_g_config->cnf_flags.no_sctp) {
TRACE_ERROR( "TCP and SCTP cannot be disabled at the same time.");
return EINVAL;
}
/* Validate local endpoints */
if ((!FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) && (fd_g_config->cnf_flags.no_ip4 || fd_g_config->cnf_flags.no_ip6)) {
struct fd_list * li;
for ( li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
struct fd_endpoint * ep = (struct fd_endpoint *)li;
if ( (fd_g_config->cnf_flags.no_ip4 && (ep->sa.sa_family == AF_INET))
||(fd_g_config->cnf_flags.no_ip6 && (ep->sa.sa_family == AF_INET6)) ) {
char sa_buf[sSA_DUMP_STRLEN];;
li = li->prev;
fd_list_unlink(&ep->chain);
fd_sa_sdump_numeric(sa_buf, &ep->sa);
LOG_N("Info: Removing local address conflicting with the flags no_IP / no_IP6 : %s", sa_buf);
free(ep);
}
}
}
/* Configure TLS default parameters */
if ((!fd_g_config->cnf_sec_data.tls_disabled) && (!fd_g_config->cnf_sec_data.prio_string)) {
const char * err_pos = NULL;
CHECK_GNUTLS_DO( gnutls_priority_init(
&fd_g_config->cnf_sec_data.prio_cache,
GNUTLS_DEFAULT_PRIORITY,
&err_pos),
{ TRACE_ERROR("Error in priority string at position : %s", err_pos); return EINVAL; } );
}
/* Verify that our certificate is valid -- otherwise remote peers will reject it */
if (!fd_g_config->cnf_sec_data.tls_disabled) {
int ret = 0, i;
gnutls_datum_t certfile;
gnutls_x509_crt_t * certs = NULL;
unsigned int cert_max = 0;
/* Read the certificate file */
FILE *stream = fopen (fd_g_config->cnf_sec_data.cert_file, "rb");
if (!stream) {
int err = errno;
TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s", fd_g_config->cnf_sec_data.cert_file, strerror(err));
return err;
}
CHECK_FCT( fd_conf_stream_to_gnutls_datum(stream, &certfile) );
fclose(stream);
/* Import the certificate(s) */
GNUTLS_TRACE( ret = gnutls_x509_crt_list_import(NULL, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) );
if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
CHECK_GNUTLS_DO(ret, return EINVAL);
}
CHECK_MALLOC( certs = calloc(cert_max, sizeof(gnutls_x509_crt_t)) );
CHECK_GNUTLS_DO( gnutls_x509_crt_list_import(certs, &cert_max, &certfile, GNUTLS_X509_FMT_PEM,
#ifdef GNUTLS_VERSION_300
GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED
#else /* GNUTLS_VERSION_300 */
0
#endif /* GNUTLS_VERSION_300 */
),
{
TRACE_ERROR("Failed to import the data from file '%s'", fd_g_config->cnf_sec_data.cert_file);
free(certfile.data);
return EINVAL;
} );
free(certfile.data);
ASSERT(cert_max >= 1);
/* Now, verify the list against the local CA and CRL */
#ifdef GNUTLS_VERSION_300
/* We use the trust list for this purpose */
{
unsigned int output;
gnutls_x509_trust_list_verify_named_crt (
fd_g_config->cnf_sec_data.trustlist,
certs[0],
fd_g_config->cnf_diamid,
fd_g_config->cnf_diamid_len,
0,
&output,
fd_conf_print_details_func);
/* if this certificate is not explicitly trusted verify against CAs
*/
if (output != 0)
{
gnutls_x509_trust_list_verify_crt (
fd_g_config->cnf_sec_data.trustlist,
certs,
cert_max,
0,
&output,
fd_conf_print_details_func);
}
if (output & GNUTLS_CERT_INVALID)
{
fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
if (output & GNUTLS_CERT_SIGNER_NOT_FOUND)
TRACE_ERROR(" - The certificate hasn't got a known issuer. Did you forget to specify TLS_CA ?");
if (output & GNUTLS_CERT_SIGNER_NOT_CA)
TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
if (output & GNUTLS_CERT_NOT_ACTIVATED)
TRACE_ERROR(" - The certificate is not yet activated.");
if (output & GNUTLS_CERT_EXPIRED)
TRACE_ERROR(" - The certificate is expired.");
return EINVAL;
}
/* Now check the subject matches our hostname */
if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid))
{
TRACE_ERROR("TLS: The certificate owner does not match the hostname '%s'", fd_g_config->cnf_diamid);
return EINVAL;
}
}
#else /* GNUTLS_VERSION_300 */
/* GnuTLS 2.x way of checking certificates */
{
gnutls_x509_crt_t * CA_list;
int CA_list_length;
gnutls_x509_crl_t * CRL_list;
int CRL_list_length;
unsigned int verify;
time_t now;
/*
GNUTLS_TRACE( gnutls_certificate_get_x509_cas (fd_g_config->cnf_sec_data.credentials, &CA_list, (unsigned int *) &CA_list_length) );
GNUTLS_TRACE( gnutls_certificate_get_x509_crls (fd_g_config->cnf_sec_data.credentials, &CRL_list, (unsigned int *) &CRL_list_length) );
CHECK_GNUTLS_DO( gnutls_x509_crt_list_verify(certs, cert_max, CA_list, CA_list_length, CRL_list, CRL_list_length, 0, &verify),
{
TRACE_ERROR("Failed to verify the local certificate '%s' against local credentials. Please check your certificate is valid.", fd_g_config->cnf_sec_data.cert_file);
return EINVAL;
} );
*/
if (verify) {
fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
if (verify & GNUTLS_CERT_INVALID)
TRACE_ERROR(" - The certificate is not trusted (unknown CA? expired?)");
if (verify & GNUTLS_CERT_REVOKED)
TRACE_ERROR(" - The certificate has been revoked.");
if (verify & GNUTLS_CERT_SIGNER_NOT_FOUND)
TRACE_ERROR(" - The certificate hasn't got a known issuer.");
if (verify & GNUTLS_CERT_SIGNER_NOT_CA)
TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
if (verify & GNUTLS_CERT_INSECURE_ALGORITHM)
TRACE_ERROR(" - The certificate signature uses a weak algorithm.");
return EINVAL;
}
/* Check the local Identity is valid with the certificate */
if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid)) {
TRACE_ERROR("TLS: Local certificate '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
TRACE_ERROR(" - The certificate hostname does not match '%s'", fd_g_config->cnf_diamid);
return EINVAL;
}
/* Check validity of all the certificates in the chain */
now = time(NULL);
for (i = 0; i < cert_max; i++)
{
time_t deadline;
GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(certs[i]) );
if ((deadline != (time_t)-1) && (deadline < now)) {
TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
TRACE_ERROR(" - The certificate %d in the chain is expired", i);
return EINVAL;
}
GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(certs[i]) );
if ((deadline != (time_t)-1) && (deadline > now)) {
TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
TRACE_ERROR(" - The certificate %d in the chain is not yet activated", i);
return EINVAL;
}
}
}
#endif /* GNUTLS_VERSION_300 */
/* Everything checked OK, free the certificate list */
for (i = 0; i < cert_max; i++)
{
GNUTLS_TRACE( gnutls_x509_crt_deinit (certs[i]) );
}
free(certs);
#ifdef GNUTLS_VERSION_300
/* Use certificate verification during the handshake */
gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
#endif /* GNUTLS_VERSION_300 */
}
/* gnutls_certificate_set_verify_limits -- so far the default values are fine... */
/* DH */
if (!fd_g_config->cnf_sec_data.tls_disabled) {
if (fd_g_config->cnf_sec_data.dh_file) {
gnutls_datum_t dhparams = { NULL, 0 };
size_t alloc = 0;
FILE *stream = fopen (fd_g_config->cnf_sec_data.dh_file, "rb");
if (!stream) {
int err = errno;
TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s", fd_g_config->cnf_sec_data.dh_file, strerror(err));
return err;
}
do {
uint8_t * realloced = NULL;
size_t read = 0;
if (alloc < dhparams.size + BUFSIZ + 1) {
alloc += alloc / 2 + BUFSIZ + 1;
CHECK_MALLOC_DO( realloced = realloc(dhparams.data, alloc),
{
free(dhparams.data);
return ENOMEM;
} )
dhparams.data = realloced;
}
read = fread( dhparams.data + dhparams.size, 1, alloc - dhparams.size - 1, stream );
dhparams.size += read;
if (ferror(stream)) {
int err = errno;
TRACE_DEBUG(INFO, "An error occurred while reading '%s': %s", fd_g_config->cnf_sec_data.dh_file, strerror(err));
return err;
}
} while (!feof(stream));
dhparams.data[dhparams.size] = '\0';
fclose(stream);
CHECK_GNUTLS_DO( gnutls_dh_params_import_pkcs3(
fd_g_config->cnf_sec_data.dh_cache,
&dhparams,
GNUTLS_X509_FMT_PEM),
{ TRACE_ERROR("Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
free(dhparams.data);
} else {
LOG_D( "Generating fresh Diffie-Hellman parameters of size %d (this takes some time)... ", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS);
CHECK_GNUTLS_DO( gnutls_dh_params_generate2(
fd_g_config->cnf_sec_data.dh_cache,
fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS),
{ TRACE_ERROR("Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
}
}
return 0;
}
#ifndef GNUTLS_VERSION_300
GCC_DIAG_ON("-Wdeprecated-declarations")
#endif /* !GNUTLS_VERSION_300 */
/* Destroy contents of fd_g_config structure */
int fd_conf_deinit()
{
TRACE_ENTRY();
if (!fd_g_config)
return 0;
/* Free the TLS parameters */
#ifdef GNUTLS_VERSION_300
gnutls_x509_trust_list_deinit(fd_g_config->cnf_sec_data.trustlist, 1);
#endif /* GNUTLS_VERSION_300 */
gnutls_priority_deinit(fd_g_config->cnf_sec_data.prio_cache);
gnutls_dh_params_deinit(fd_g_config->cnf_sec_data.dh_cache);
gnutls_certificate_free_credentials(fd_g_config->cnf_sec_data.credentials);
free(fd_g_config->cnf_sec_data.cert_file); fd_g_config->cnf_sec_data.cert_file = NULL;
free(fd_g_config->cnf_sec_data.key_file); fd_g_config->cnf_sec_data.key_file = NULL;
free(fd_g_config->cnf_sec_data.ca_file); fd_g_config->cnf_sec_data.ca_file = NULL;
free(fd_g_config->cnf_sec_data.crl_file); fd_g_config->cnf_sec_data.crl_file = NULL;
free(fd_g_config->cnf_sec_data.prio_string); fd_g_config->cnf_sec_data.prio_string = NULL;
free(fd_g_config->cnf_sec_data.dh_file); fd_g_config->cnf_sec_data.dh_file = NULL;
/* Destroy dictionary */
CHECK_FCT_DO( fd_dict_fini(&fd_g_config->cnf_dict), );
/* Destroy the main event queue */
CHECK_FCT_DO( fd_fifo_del(&fd_g_config->cnf_main_ev), );
/* Destroy the local endpoints and applications */
CHECK_FCT_DO(fd_ep_filter(&fd_g_config->cnf_endpoints, 0 ), );
CHECK_FCT_DO(fd_app_empty(&fd_g_config->cnf_apps ), );
/* Destroy the local identity */
free(fd_g_config->cnf_diamid); fd_g_config->cnf_diamid = NULL;
free(fd_g_config->cnf_diamrlm); fd_g_config->cnf_diamrlm = NULL;
return 0;
}
+403
View File
@@ -0,0 +1,403 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
GCC_DIAG_OFF("-Wdeprecated-declarations")
#include <gcrypt.h>
GCC_DIAG_ON("-Wdeprecated-declarations")
/* hash: session-id===>port id */
GHashTable *g_sess_hash = NULL;
/* The static configuration structure */
static struct fd_config g_conf;
struct fd_config * fd_g_config = NULL;
/* gcrypt functions to support posix threads */
#ifndef GNUTLS_VERSION_210
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif /* GNUTLS_VERSION_210 */
/* Thread that process incoming events on the main queue -- and terminates the framework when requested */
static pthread_t core_runner = (pthread_t)NULL;
/* Signal extensions when the framework is completely initialized (they are waiting in fd_core_waitstartcomplete()) */
static enum core_state {
CORE_NOT_INIT, /* initial state */
CORE_LIBS_INIT, /* fd_core_initialize was called */
CORE_CONF_READY,/* Configuration was parsed, extensions are loaded */
CORE_RUNNING, /* Servers and clients are started, core_runner thread is running */
CORE_SHUTDOWN, /* The framework is terminating all objects */
CORE_TERM /* Shutdown complete. */
} core_state = CORE_NOT_INIT;
static pthread_mutex_t core_mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t core_cnd = PTHREAD_COND_INITIALIZER;
static enum core_state core_state_get(void)
{
enum core_state cur_state;
CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), );
cur_state = core_state;
CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), );
return cur_state;
}
static void core_state_set(enum core_state newstate)
{
CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), );
LOG_D("Core state: %d -> %d", core_state, newstate);
core_state = newstate;
CHECK_POSIX_DO( pthread_cond_broadcast( &core_cnd ), );
CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), );
}
static int core_state_wait(enum core_state waitstate)
{
int ret = 0;
CHECK_POSIX( pthread_mutex_lock( &core_mtx ));
pthread_cleanup_push( fd_cleanup_mutex, &core_mtx );
while (waitstate > core_state) {
CHECK_POSIX_DO(ret = pthread_cond_wait(&core_cnd, &core_mtx), break);
}
pthread_cleanup_pop( 0 );
CHECK_POSIX( pthread_mutex_unlock( &core_mtx ));
return ret;
}
static void core_shutdown(void)
{
LOG_N( FD_PROJECT_BINARY " framework is stopping...");
fd_log_threadname("fD Core Shutdown");
/* cleanups */
CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ );
CHECK_FCT_DO( fd_rtdisp_cleanstop(), /* Stop dispatch thread(s) after a clean loop if possible */ );
CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ );
CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ );
CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ );
CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ );
GNUTLS_TRACE( gnutls_global_deinit() );
CHECK_FCT_DO( fd_conf_deinit(), );
CHECK_FCT_DO( fd_event_trig_fini(), );
fd_log_debug(FD_PROJECT_BINARY " framework is terminated.");
fd_libproto_fini();
g_hash_table_destroy(g_sess_hash);
}
static void * core_runner_thread(void * arg)
{
char cause[128]={"no cb"};
fd_log_threadname("fD Core Runner");
core_state_wait(CORE_RUNNING);
/* Handle events incoming on the main event queue */
while (1) {
int code; size_t sz; void * data;
CHECK_FCT_DO( fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data), break );
switch (code) {
case FDEV_TRIGGER:
{
int tv, *p;
CHECK_PARAMS_DO( sz == sizeof(int),
{
TRACE_DEBUG(NONE, "Internal error: got FDEV_TRIGGER without trigger value!");
ASSERT(0);
sprintf(cause,"Internal error: got FDEV_TRIGGER without trigger value!");
goto end;
} );
p = data;
tv = *p;
free(p);
CHECK_FCT_DO( fd_event_trig_call_cb(tv), goto end );
}
break;
case FDEV_TERMINATE_INT:
sprintf(cause,"Internal error: got FDEV_TERMINATE_INT!");
goto end;
default:
TRACE_DEBUG(INFO, "Unexpected event in the main event queue (%d), ignored.", code);
}
}
end:
core_shutdown();
if(1)
{
FILE *fp=NULL;
fp = fopen("fd_cause.txt","w");
if(fp)
{
fprintf(fp,"%s\r\n",cause);
fclose(fp);
}
exit(12);
}
return NULL;
}
/*********************************/
/* Public interfaces */
/* Initialize the libfdcore internals. This also initializes libfdproto */
int fd_core_initialize(void)
{
int ret;
if (core_state_get() != CORE_NOT_INIT) {
fprintf(stderr, "fd_core_initialize() called more than once!\n");
return EINVAL;
}
/* Initialize the library -- must come first since it initializes the debug facility */
ret = fd_libproto_init();
if (ret != 0) {
fprintf(stderr, "Unable to initialize libfdproto: %s\n", strerror(ret));
return ret;
}
/* Name this thread */
fd_log_threadname("Main");
LOG_N("libfdproto '%s' initialized.", fd_libproto_version);
/* Initialize gcrypt and gnutls */
#ifndef GNUTLS_VERSION_210
GNUTLS_TRACE( (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread) );
GNUTLS_TRACE( (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0) );
#endif /* GNUTLS_VERSION_210 */
CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL );
if ( ! gnutls_check_version(GNUTLS_VERSION) ) {
TRACE_ERROR( "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'", gnutls_check_version(NULL));
return EINVAL;
} else {
#ifdef GNUTLS_VERSION_210
TRACE_DEBUG(INFO, "libgnutls '%s' initialized.", gnutls_check_version(NULL) );
#else /* GNUTLS_VERSION_210 */
TRACE_DEBUG(INFO, "libgnutls '%s', libgcrypt '%s', initialized.", gnutls_check_version(NULL), gcry_check_version(NULL) );
#endif /* GNUTLS_VERSION_210 */
}
/* Initialize the config with default values */
memset(&g_conf, 0, sizeof(struct fd_config));
fd_g_config = &g_conf;
CHECK_FCT( fd_conf_init() );
/* Add definitions of the base protocol */
CHECK_FCT( fd_dict_base_protocol(fd_g_config->cnf_dict) );
/* Initialize some modules */
CHECK_FCT( fd_hooks_init() );
CHECK_FCT( fd_queues_init() );
CHECK_FCT( fd_sess_start() );
CHECK_FCT( fd_p_expi_init() );
core_state_set(CORE_LIBS_INIT);
LOG_N("libfdcore '%s' initialized.", fd_core_version);
/* Next thing is to parse the config, leave this for a different function */
return 0;
}
static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER;
/* Parse the freeDiameter.conf configuration file, load the extensions */
static int fd_core_parseconf_int(const char * conffile)
{
char * buf = NULL, *b;
size_t len = 0, offset=0;
TRACE_ENTRY("%p", conffile);
/* Conf file */
if (conffile)
fd_g_config->cnf_file = conffile; /* otherwise, we use the default name */
CHECK_FCT( fd_conf_parse() );
/* The following module use data from the configuration */
CHECK_FCT( fd_rtdisp_init() );
/* Now, load all dynamic extensions */
CHECK_FCT( fd_ext_load() );
/* Display configuration */
b = fd_conf_dump(&buf, &len, NULL);
LOG_SPLIT(FD_LOG_NOTICE, NULL, b ?: "<Error during configuration dump...>", NULL);
/* Display extensions status */
b = fd_ext_dump(&buf, &len, NULL);
LOG_SPLIT(FD_LOG_NOTICE, "Loaded extensions: ", b?:"<Error during extensions dump...>", NULL);
/* Display registered triggers for FDEV_TRIGGER */
b = fd_event_trig_dump(&buf, &len, &offset);
if (!b || offset) {
LOG_N("%s", b ?: "Error during triggers dump...");
}
free(buf);
/* Since some extensions might have modified the definitions from the dict_base_protocol, we only load the objects now */
CHECK_FCT( fd_msg_init() );
/* Ok, ready for next step */
core_state_set(CORE_CONF_READY);
return 0;
}
int fd_core_parseconf(const char * conffile)
{
int ret;
CHECK_POSIX( pthread_mutex_lock(&core_lock) );
ret = fd_core_parseconf_int(conffile);
CHECK_POSIX( pthread_mutex_unlock(&core_lock) );
return ret;
}
/* For threads that would need to wait complete start of the framework (ex: in extensions) */
int fd_core_waitstartcomplete(void)
{
TRACE_ENTRY("");
return core_state_wait(CORE_RUNNING);
}
/*static void free_key(gpointer data)
{
//printf("We free key: %s /n", data);
free(data);
}*/
/* Start the server & client threads */
static int fd_core_start_int(void)
{
/* create hash: session id===>port id */
g_sess_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
/* Start server threads */
CHECK_FCT( fd_servers_start() );
/* Start the peer state machines */
CHECK_FCT( fd_psm_start() );
/* Start the core runner thread that handles main events (until shutdown) */
CHECK_POSIX( pthread_create(&core_runner, NULL, core_runner_thread, NULL) );
/* Unlock threads waiting into fd_core_waitstartcomplete */
core_state_set(CORE_RUNNING);
/* Ok, everything is running now... */
return 0;
}
int fd_core_start(void)
{
int ret;
CHECK_POSIX( pthread_mutex_lock(&core_lock) );
ret = fd_core_start_int();
CHECK_POSIX( pthread_mutex_unlock(&core_lock) );
return ret;
}
/* Initialize shutdown of the framework. This is not blocking. */
int fd_core_shutdown(void)
{
enum core_state cur_state = core_state_get();
LOG_F("Initiating freeDiameter shutdown sequence (%d)", cur_state);
if (cur_state < CORE_RUNNING) {
/* Calling application must make sure the initialization is not ongoing in a separate thread... */
if (pthread_mutex_lock(&core_lock) != 0) {
/* This function must not be called asynchronously from fd_core_parseconf / fd_core_start ! Please review your main app design */
ASSERT(0);
return EINVAL;
}
core_shutdown();
core_state_set(CORE_TERM);
pthread_mutex_unlock(&core_lock);
} else if (cur_state == CORE_RUNNING) {
core_state_set(CORE_SHUTDOWN);
CHECK_FCT( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE_INT, 0, NULL) );
}
/* Other case, the framework is already shutting down */
return 0;
}
/* Wait for the shutdown to be complete -- this must be called after fd_core_shutdown to reclaim some resources. */
int fd_core_wait_shutdown_complete(void)
{
enum core_state cur_state = core_state_get();
void * th_ret = NULL;
CHECK_FCT(core_state_wait(CORE_SHUTDOWN));
if (cur_state == CORE_TERM)
return 0;
/* Just wait for core_runner_thread to complete and return gracefully */
CHECK_POSIX(pthread_join(core_runner, &th_ret));
core_state_set(CORE_TERM);
return 0;
}
File diff suppressed because it is too large. Load diff
+271
View File
@@ -0,0 +1,271 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* Add an endpoint information in a list */
int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags )
{
struct fd_endpoint * ep;
struct fd_list * li;
union {
sSA * sa;
sSA4 *sin;
sSA6 *sin6;
} ptr;
in_port_t * port;
int cmp = -1;
TRACE_ENTRY("%p %p %u %x", list, sa, sl, flags);
CHECK_PARAMS( list && sa && (sl <= sizeof(sSS)) );
if (list->next == NULL) {
/* the list is not initialized yet, do it */
fd_list_init(list, NULL);
}
ptr.sa = sa;
/* Filter out a bunch of invalid addresses */
switch (sa->sa_family) {
case AF_INET:
if (! (flags & EP_ACCEPTALL)) {
if (IN_IS_ADDR_UNSPECIFIED(&ptr.sin->sin_addr)
|| IN_IS_ADDR_LOOPBACK(&ptr.sin->sin_addr)
/* the next one filters both EXPERIMENTAL, BADCLASS and MULTICAST. */
|| ((ntohl(ptr.sin->sin_addr.s_addr) & 0xe0000000) == 0xe0000000)
|| (ptr.sin->sin_addr.s_addr == INADDR_BROADCAST)) {
LOG_A(" DEBUG:fd_ep_add_merge Address was ignored, not added.");
return 0;
}
}
port = &ptr.sin->sin_port;
break;
case AF_INET6:
if (! (flags & EP_ACCEPTALL)) {
if (IN6_IS_ADDR_UNSPECIFIED(&ptr.sin6->sin6_addr)
|| IN6_IS_ADDR_LOOPBACK(&ptr.sin6->sin6_addr)
|| IN6_IS_ADDR_MULTICAST(&ptr.sin6->sin6_addr)
|| IN6_IS_ADDR_LINKLOCAL(&ptr.sin6->sin6_addr)
|| IN6_IS_ADDR_SITELOCAL(&ptr.sin6->sin6_addr)) {
LOG_A(" DEBUG:fd_ep_add_merge Address was ignored, not added.");
return 0;
}
}
port = &ptr.sin6->sin6_port;
break;
default:
LOG_A(" DEBUG:fd_ep_add_merge Address family was unknown, not added.");
return 0;
}
/* remove the ACCEPTALL flag */
flags &= ~EP_ACCEPTALL;
/* Search place in the list */
for (li = list->next; li != list; li = li->next) {
ep = (struct fd_endpoint *)li;
in_port_t * ep_port;
/* First, compare the address family */
if (ep->sa.sa_family < sa->sa_family)
continue;
if (ep->sa.sa_family > sa->sa_family)
break;
/* Then compare the address field */
switch (sa->sa_family) {
case AF_INET:
cmp = memcmp(&ep->sin.sin_addr, &ptr.sin->sin_addr, sizeof(struct in_addr));
ep_port = &ep->sin.sin_port;
break;
case AF_INET6:
cmp = memcmp(&ep->sin6.sin6_addr, &ptr.sin6->sin6_addr, sizeof(struct in6_addr));
ep_port = &ep->sin6.sin6_port;
break;
default:
ASSERT( 0 ); /* we got a different value previously in this same function */
}
if (cmp < 0)
continue;
if (cmp > 0)
break;
/* Finally compare the port, only if not 0 */
if (*port == 0)
break;
if (*ep_port == 0) {
/* save the port information in the list, and break */
*ep_port = *port;
break;
}
if (*ep_port < *port) {
cmp = -1;
continue;
}
if (*ep_port > *port)
cmp = 1;
break;
}
if (cmp) {
/* new item to be added */
CHECK_MALLOC( ep = malloc(sizeof(struct fd_endpoint)) );
memset(ep, 0, sizeof(struct fd_endpoint));
fd_list_init(&ep->chain, NULL);
memcpy(&ep->ss, sa, sl);
/* Insert in the list */
fd_list_insert_before(li, &ep->chain);
}
/* Merge the flags */
ep->flags |= flags;
return 0;
}
/* Delete endpoints that do not have a matching flag from a list (0: delete all endpoints) */
int fd_ep_filter( struct fd_list * list, uint32_t flags )
{
struct fd_list * li;
TRACE_ENTRY("%p %x", list, flags);
CHECK_PARAMS(list);
for (li = list->next; li != list; li = li->next) {
struct fd_endpoint * ep = (struct fd_endpoint *)li;
if (! (ep->flags & flags)) {
li = li->prev;
fd_list_unlink(&ep->chain);
free(ep);
}
}
return 0;
}
/* Keep only endpoints of the same family as af */
int fd_ep_filter_family( struct fd_list * list, int af )
{
struct fd_list * li;
TRACE_ENTRY("%p %d", list, af);
CHECK_PARAMS(list);
for (li = list->next; li != list; li = li->next) {
struct fd_endpoint * ep = (struct fd_endpoint *)li;
if (ep->sa.sa_family != af) {
li = li->prev;
fd_list_unlink(&ep->chain);
free(ep);
}
}
return 0;
}
/* Reset the given flag(s) from all items in the list */
int fd_ep_clearflags( struct fd_list * list, uint32_t flags )
{
struct fd_list * li;
TRACE_ENTRY("%p %x", list, flags);
CHECK_PARAMS(list);
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;
}
DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump_one, int preamble, struct fd_endpoint * ep )
{
FD_DUMP_HANDLE_OFFSET();
if (preamble) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{ep}(@%p): ", ep), return NULL);
}
if (!ep) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
return *buf;
}
CHECK_MALLOC_DO( fd_sa_dump( FD_DUMP_STD_PARAMS, &ep->sa, NI_NUMERICHOST | NI_NUMERICSERV ), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{%s%s%s%s%s}",
(ep->flags & EP_FL_CONF) ? "C" : "-",
(ep->flags & EP_FL_DISC) ? "D" : "-",
(ep->flags & EP_FL_ADV) ? "A" : "-",
(ep->flags & EP_FL_LL) ? "L" : "-",
(ep->flags & EP_FL_PRIMARY) ? "P" : "-"), return NULL);
return *buf;
}
DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump, int preamble, int indent, struct fd_list * eps )
{
struct fd_list * li;
FD_DUMP_HANDLE_OFFSET();
if (preamble) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%*s{eps}(@%p):", indent, "", eps), return NULL);
}
if (eps) {
for (li = eps->next; li != eps; li = li->next) {
struct fd_endpoint * ep = (struct fd_endpoint *)li;
if (preamble) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*s", indent+1, ""), return NULL);
} else if (li->prev != eps) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\t"), return NULL);
}
CHECK_MALLOC_DO( fd_ep_dump_one( FD_DUMP_STD_PARAMS, preamble, ep ), return NULL);
}
}
return *buf;
}
+231
View File
@@ -0,0 +1,231 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* Events are a subset of fifo queues, with a known type */
int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data)
{
struct fd_event * ev;
CHECK_MALLOC( ev = malloc(sizeof(struct fd_event)) );
ev->code = code;
ev->size = datasz;
ev->data = data;
CHECK_FCT( fd_fifo_post(queue, &ev) );
return 0;
}
int fd_event_get(struct fifo *queue, int *code, size_t *datasz, void ** data)
{
struct fd_event * ev;
CHECK_FCT( fd_fifo_get(queue, &ev) );
if (code)
*code = ev->code;
if (datasz)
*datasz = ev->size;
if (data)
*data = ev->data;
free(ev);
return 0;
}
int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int *code, size_t *datasz, void ** data)
{
struct fd_event * ev;
int ret = 0;
ret = fd_fifo_timedget(queue, &ev, timeout);
if (ret == ETIMEDOUT) {
if (code)
*code = timeoutcode;
if (datasz)
*datasz = 0;
if (data)
*data = NULL;
} else {
CHECK_FCT( ret );
if (code)
*code = ev->code;
if (datasz)
*datasz = ev->size;
if (data)
*data = ev->data;
free(ev);
}
return 0;
}
void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data))
{
struct fd_event * ev;
/* Purge all events, and free the associated data if any */
while (fd_fifo_tryget( *queue, &ev ) == 0) {
(*free_cb)(ev->data);
free(ev);
}
CHECK_FCT_DO( fd_fifo_del(queue), /* continue */ );
return ;
}
const char * fd_ev_str(int event)
{
switch (event) {
#define case_str( _val )\
case _val : return #_val
case_str(FDEV_TERMINATE_INT);
case_str(FDEV_TRIGGER);
default:
TRACE_DEBUG(FULL, "Unknown event : %d", event);
return "Unknown event";
}
}
/**********************************************************************/
/* Trigged events */
/* This allows extensions to register for / send triggers to other extensions */
/* It is used for example for users interactions (through signals or ...) */
/* Because the number of triggers is not expected to grow, we use a simple ordered chained list */
static pthread_rwlock_t trig_rwl = PTHREAD_RWLOCK_INITIALIZER;
static struct fd_list trig_list = FD_LIST_INITIALIZER(trig_list); /* The list of triggers ordered by trigger value */
struct trig_item {
struct fd_list chain;
int trig_value;
const char * trig_module;
void (*cb)(void);
};
/* Add a new entry in the trigger list */
int fd_event_trig_regcb(int trigger_val, const char * module, void (*cb)(void))
{
struct trig_item * ti;
struct fd_list * li;
TRACE_ENTRY("%d %p %p", trigger_val, module, cb);
CHECK_PARAMS( trigger_val && cb );
/* Create a new trig_item */
CHECK_MALLOC( ti = malloc(sizeof(struct trig_item)) );
memset(ti, 0, sizeof(struct trig_item));
fd_list_init(&ti->chain, ti);
ti->trig_value = trigger_val;
ti->trig_module = module;
ti->cb = cb;
/* Now insert in the list */
CHECK_POSIX( pthread_rwlock_wrlock(&trig_rwl) );
for (li = trig_list.next; li != &trig_list; li = li->next) {
struct trig_item *t = li->o;
if (t->trig_value >= trigger_val)
break;
}
fd_list_insert_before(li, &ti->chain);
CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
return 0;
}
DECLARE_FD_DUMP_PROTOTYPE(fd_event_trig_dump)
{
struct fd_list * li;
FD_DUMP_HANDLE_OFFSET();
CHECK_POSIX_DO( pthread_rwlock_rdlock(&trig_rwl), );
for (li = trig_list.next; li != &trig_list; li = li->next) {
struct trig_item *t = li->o;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{signal:%d}'%s'->%p ", t->trig_value, t->trig_module, t->cb), break);
}
CHECK_POSIX_DO( pthread_rwlock_unlock(&trig_rwl), );
return *buf;
}
static void *call_cb_detached(void * arg)
{
void (*cb)(void) = arg;
fd_log_threadname("Trig'd callback thread");
TRACE_ENTRY("%p", arg);
(*cb)();
TRACE_DEBUG(ANNOYING, "Callback %p completed", cb);
return NULL;
}
int fd_event_trig_call_cb(int trigger_val)
{
struct fd_list * li;
pthread_attr_t detached;
pthread_t th;
CHECK_POSIX( pthread_attr_init(&detached) );
CHECK_POSIX( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) );
CHECK_POSIX( pthread_rwlock_rdlock(&trig_rwl) );
for (li = trig_list.next; li != &trig_list; li = li->next) {
struct trig_item *t = li->o;
if (t->trig_value == trigger_val) {
TRACE_DEBUG(FULL, "Trigger %d: Calling %p in %s", t->trig_value, t->cb, t->trig_module);
CHECK_POSIX_DO( pthread_create( &th, &detached, call_cb_detached, t->cb ), break );
}
if (t->trig_value > trigger_val)
break;
}
CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
return 0;
}
int fd_event_trig_fini(void) {
TRACE_ENTRY("");
CHECK_POSIX( pthread_rwlock_wrlock(&trig_rwl) );
while (!FD_IS_LIST_EMPTY(&trig_list)) {
struct fd_list * li = trig_list.next;
fd_list_unlink(li);
free(li);
}
CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
return 0;
}
+293
View File
@@ -0,0 +1,293 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
#include <dlfcn.h> /* We may use libtool's <ltdl.h> later for better portability.... */
#include <libgen.h> /* for "basename" */
/* plugins management */
/* List of extensions to load, from the configuration parsing */
struct fd_ext_info {
struct fd_list chain; /* link in the list */
char *filename; /* extension filename. must be a dynamic library with fd_ext_init symbol. */
char *conffile; /* optional configuration file name for the extension */
void *handler; /* object returned by dlopen() */
const char **depends; /* names of the other extensions this one depends on (if provided) */
char *ext_name; /* points to the extension name, either inside depends, or basename(filename) */
int free_ext_name; /* must be freed if it was malloc'd */
void (*fini)(void); /* optional address of the fd_ext_fini callback */
};
/* list of extensions */
static struct fd_list ext_list = FD_LIST_INITIALIZER(ext_list);
/* Add new extension */
int fd_ext_add( char * filename, char * conffile )
{
struct fd_ext_info * new;
TRACE_ENTRY("%p %p", filename, conffile);
/* Check the filename is valid */
CHECK_PARAMS( filename );
/* Create a new object in the list */
CHECK_MALLOC( new = malloc( sizeof(struct fd_ext_info) ) );
memset(new, 0, sizeof(struct fd_ext_info));
fd_list_init(&new->chain, NULL);
new->filename = filename;
new->conffile = conffile;
fd_list_insert_before( &ext_list, &new->chain );
TRACE_DEBUG (FULL, "Extension %s added to the list.", filename);
return 0;
}
/* Dump the list */
DECLARE_FD_DUMP_PROTOTYPE(fd_ext_dump)
{
struct fd_list * li;
FD_DUMP_HANDLE_OFFSET();
if (FD_IS_LIST_EMPTY(&ext_list)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "-none-"), return NULL);
} else {
for (li = ext_list.next; li != &ext_list; li = li->next)
{
struct fd_ext_info * ext = (struct fd_ext_info *)li;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'[%s], %sloaded%s",
ext->filename,
ext->conffile?:"(no config file)",
ext->handler ? "" : "not ", (li->next == &ext_list) ? "":"\n"), return NULL);
}
}
return *buf;
}
/* Check the dependencies. The object must have been dlopened already. */
#if 0
static int check_dependencies(struct fd_ext_info * ext)
{
int i = 1;
TRACE_ENTRY( "%p", ext );
/* Attempt to resolve the dependency array */
ext->depends = dlsym( ext->handler, "fd_ext_depends" );
if (!ext->depends) {
/* Duplicate the filename */
char * tmp = strdup(ext->filename);
ext->ext_name = strdup(basename(tmp));
free(tmp);
ext->free_ext_name = 1;
TRACE_DEBUG(FULL, "Old extension's [%s] API: missing dependencies (ignored)", ext->ext_name);
return 0;
}
ext->ext_name = (char *)ext->depends[0];
TRACE_DEBUG(FULL, "Checking dependencies for '%s'...", ext->ext_name);
while (ext->depends[i]) {
struct fd_list * li;
for (li = ext_list.next; li != &ext->chain; li = li->next)
{
struct fd_ext_info * e = (struct fd_ext_info *)li;
if (!strcasecmp(e->ext_name, ext->depends[i])) {
/* the dependency was already loaded */
break;
}
}
if (li == &ext->chain) {
/* the dependency was not found */
LOG_F("Error: extension [%s] depends on [%s] which was not loaded first. Please fix your configuration file.",
ext->ext_name, ext->depends[i]);
return ESRCH;
}
i++;
}
/* All dependencies resolved successfully */
return 0;
}
#endif
/* Load all extensions in the list */
/* fj added 2016/06/22 */
#if 1
extern int fd_ext_init(int major, int minor, char * conffile);
int fd_ext_load()
{
TRACE_ENTRY();
fd_ext_init(FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, NULL);
return 0;
}
#else
int fd_ext_load()
{
int ret;
int (*fd_ext_init)(int, int, char *) = NULL;
struct fd_list * li;
TRACE_ENTRY();
/* Loop on all extensions */
for (li = ext_list.next; li != &ext_list; li = li->next)
{
struct fd_ext_info * ext = (struct fd_ext_info *)li;
LOG_D( "Loading : %s", ext->filename);
/* Load the extension */
#ifndef DEBUG
ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
#else /* DEBUG */
/* We resolve symbols immediatly so it's easier to find problems in ABI */
ext->handler = dlopen(ext->filename, RTLD_NOW | RTLD_GLOBAL);
#endif /* DEBUG */
if (ext->handler == NULL) {
/* An error occured */
LOG_F("Loading of extension %s failed: %s", ext->filename, dlerror());
ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
if (ext->handler) {
if (!check_dependencies(ext)) {
LOG_F("In addition, not all declared dependencies are satisfied (Internal Error!)");
}
}
return EINVAL;
}
/* Check if declared dependencies are satisfied. */
CHECK_FCT( check_dependencies(ext) );
/* Resolve the entry point of the extension */
fd_ext_init = ( int (*) (int, int, char *) )dlsym( ext->handler, "fd_ext_init" );
if (fd_ext_init == NULL) {
/* An error occured */
TRACE_ERROR("Unable to resolve symbol 'fd_ext_init' for extension %s: %s", ext->filename, dlerror());
return EINVAL;
}
/* Resolve the exit point of the extension, which is optional for extensions */
ext->fini = ( void (*) (void) )dlsym( ext->handler, "fd_ext_fini" );
if (ext->fini == NULL) {
/* Not provided */
TRACE_DEBUG (FULL, "Extension [%s] has no fd_ext_fini function.", ext->filename);
} else {
/* Provided */
TRACE_DEBUG (FULL, "Extension [%s] fd_ext_fini has been resolved successfully.", ext->filename);
}
/* Now call the entry point to initialize the extension */
ret = (*fd_ext_init)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, ext->conffile );
if (ret != 0) {
/* The extension was unable to load cleanly */
TRACE_ERROR("Extension %s returned an error during initialization: %s", ext->filename, strerror(ret));
return ret;
}
/* Proceed to the next extension */
}
LOG_N("All extensions loaded.");
/* We have finished. */
return 0;
}
#endif
/* Now unload the extensions and free the memory */
/* fj added 2016/06/22 */
#if 1
extern void fd_ext_fini(void);
int fd_ext_term( void )
{
TRACE_ENTRY();
fd_ext_fini();
return 0;
}
#else
int fd_ext_term( void )
{
TRACE_ENTRY();
/* Loop on all extensions, in FIFO order */
while (!FD_IS_LIST_EMPTY(&ext_list))
{
struct fd_list * li = ext_list.next;
struct fd_ext_info * ext = (struct fd_ext_info *)li;
/* Unlink this element from the list */
fd_list_unlink(li);
/* Call the exit point of the extension, if it was resolved */
if (ext->fini != NULL) {
TRACE_DEBUG (FULL, "Calling [%s]->fd_ext_fini function.", ext->ext_name ?: ext->filename);
(*ext->fini)();
}
#ifndef SKIP_DLCLOSE
/* Now unload the extension */
if (ext->handler) {
TRACE_DEBUG (FULL, "Unloading %s", ext->ext_name ?: ext->filename);
if ( dlclose(ext->handler) != 0 ) {
TRACE_DEBUG (INFO, "Unloading [%s] failed : %s", ext->ext_name ?: ext->filename, dlerror());
}
}
#endif /* SKIP_DLCLOSE */
/* Free the object and continue */
if (ext->free_ext_name)
free(ext->ext_name);
free(ext->filename);
free(ext->conffile);
free(ext);
}
/* We always return 0 since we would not handle an error anyway... */
return 0;
}
#endif
+375
View File
@@ -0,0 +1,375 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
/* This file contains the definitions for internal use in the freeDiameter core library */
#ifndef _FDCORE_INTERNAL_H
#define _FDCORE_INTERNAL_H
#include "freeDiameter-host.h"
#include "libfdcore.h"
#ifdef DISABLE_SCTP
#undef IPPROTO_SCTP
#define IPPROTO_SCTP (2 = 4) /* some compilation error to spot the references */
#endif /* DISABLE_SCTP */
#ifndef HAVE_AI_ADDRCONFIG
#define AI_ADDRCONFIG 0 /* ignore this flag at the moment */
#endif /* HAVE_AI_ADDRCONFIG */
/* Timeout for establishing a connection */
#ifndef CNX_TIMEOUT
#define CNX_TIMEOUT 10 /* in seconds */
#endif /* CNX_TIMEOUT */
/* Timeout for receiving a CER after incoming connection is established */
#ifndef INCNX_TIMEOUT
#define INCNX_TIMEOUT 20 /* in seconds */
#endif /* INCNX_TIMEOUT */
/* Timeout for receiving a CEA after CER is sent */
#ifndef CEA_TIMEOUT
#define CEA_TIMEOUT 20 /* in seconds */
#endif /* CEA_TIMEOUT */
/* The timeout value to wait for answer to a DPR */
#ifndef DPR_TIMEOUT
#define DPR_TIMEOUT 15 /* in seconds */
#endif /* DPR_TIMEOUT */
/* Delay where the connection is maintained opened to allow exchanging remaining pending answers after DPR/DPA */
#ifndef GRACE_TIMEOUT
#define GRACE_TIMEOUT 1 /* in seconds */
#endif /* GRACE_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();
int fd_conf_deinit();
int fd_conf_parse();
int fddparse(struct fd_config * conf); /* yacc generated */
int fd_conf_stream_to_gnutls_datum(FILE * pemfile, gnutls_datum_t *out);
/* Extensions */
int fd_ext_add( char * filename, char * conffile );
int fd_ext_load();
int fd_ext_term(void);
/* Messages */
int fd_msg_init(void);
extern struct dict_object * fd_dict_avp_OSI; /* Origin-State-Id */
extern struct dict_object * fd_dict_cmd_CER; /* Capabilities-Exchange-Request */
extern struct dict_object * fd_dict_cmd_DWR; /* Device-Watchdog-Request */
extern struct dict_object * fd_dict_avp_DC; /* Disconnect-Cause */
extern struct dict_object * fd_dict_cmd_DPR; /* Disconnect-Peer-Request */
/* Global message queues */
extern struct fifo * fd_g_incoming; /* all messages received from other peers, except local messages (CER, ...) */
extern struct fifo * fd_g_outgoing; /* messages to be sent to other peers on the network following routing procedure */
extern struct fifo * fd_g_local; /* messages to be handled to local extensions */
/* Message queues */
int fd_queues_init(void);
int fd_queues_fini(struct fifo ** queue);
/* Trigged events */
int fd_event_trig_call_cb(int trigger_val);
int fd_event_trig_fini(void);
/* Create all the dictionary objects defined in the Diameter base RFC. */
int fd_dict_base_protocol(struct dictionary * dict);
/* Routing */
int fd_rtdisp_init(void);
int fd_rtdisp_cleanstop(void);
int fd_rtdisp_fini(void);
int fd_rtdisp_cleanup(void);
/* Sentinel for the sent requests list */
struct sr_list {
struct fd_list srs; /* requests ordered by hop-by-hop id */
struct fd_list exp; /* requests that have a timeout set, ordered by timeout */
long cnt; /* number of requests in the srs list */
long cnt_lost; /* number of requests that have not been answered in time.
It is decremented when an unexpected answer is received, so this may not be accurate. */
pthread_mutex_t mtx; /* mutex to protect these lists */
pthread_cond_t cnd; /* cond var used by the thread that handles timeouts */
pthread_t thr; /* the thread that handles timeouts (expirecb called in separate forked threads) */
};
/* Peers */
struct fd_peer { /* The "real" definition of the peer structure */
/* The public data */
struct peer_hdr p_hdr;
/* Eye catcher, EYEC_PEER */
int p_eyec;
#define EYEC_PEER 0x373C9336
/* Origin of this peer object, for debug */
char *p_dbgorig;
/* State of the peer, and its lock */
enum peer_state p_state;
pthread_mutex_t p_state_mtx;
/* Chaining in peers sublists */
struct fd_list p_actives; /* list of peers in the STATE_OPEN state -- used by routing */
struct fd_list p_expiry; /* list of expiring peers, ordered by their timeout value */
struct timespec p_exp_timer; /* Timestamp where the peer will expire; updated each time activity is seen on the peer (except DW) */
/* Some flags influencing the peer state machine */
struct {
unsigned pf_responder : 1; /* The peer has been created to handle incoming connection */
unsigned pf_delete : 1; /* Destroy the peer when the connection is terminated */
unsigned pf_localterm : 1; /* If the latest DPR/DPA was initiated from this side */
unsigned pf_dw_pending : 1; /* A DWR message was sent and not answered yet */
unsigned pf_cnx_pb : 1; /* The peer was disconnected because of watchdogs; must exchange 3 watchdogs before putting back to normal */
unsigned pf_reopen_cnt : 2; /* remaining DW to be exchanged after re-established connection */
} p_flags;
/* The events queue, peer state machine thread, timer for states timeouts */
struct fifo *p_events; /* The mutex of this FIFO list protects also the state and timer information */
pthread_t p_psm;
struct timespec p_psm_timer;
/* Outgoing message queue, and thread managing sending the messages */
struct fifo *p_tosend;
pthread_t p_outthr;
/* The next hop-by-hop id value for the link, only read & modified by p_outthr */
uint32_t p_hbh;
/* Sent requests (for fallback), list of struct sentreq ordered by hbh */
struct sr_list p_sr;
struct fifo *p_tofailover;
/* Pending received requests not yet answered (count only) */
long p_reqin_count; /* We use p_state_mtx to protect this value */
/* Data for transitional states before the peer is in OPEN state */
struct {
struct cnxctx * p_receiver; /* Only used in case of election */
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 */
};
/* connection context: socket and related information */
struct cnxctx *p_cnxctx;
/* Callback for peer validation after the handshake */
int (*p_cb2)(struct peer_info *);
/* Callback on initial connection success / failure after the peer was added */
void (*p_cb)(struct peer_info *, void *);
void *p_cb_data;
};
#define CHECK_PEER( _p ) \
(((_p) != NULL) && (((struct fd_peer *)(_p))->p_eyec == EYEC_PEER))
#define fd_peer_getstate(peer) fd_peer_get_state((struct peer_hdr *)(peer))
/* Events codespace for struct fd_peer->p_events */
enum {
/* request to terminate this peer : disconnect, requeue all messages */
FDEVP_TERMINATE = 1500
/* A connection object has received a message. (data contains the buffer + padding + struct fd_msg_pmdl) */
,FDEVP_CNX_MSG_RECV
/* A connection object has encountered an error (disconnected). */
,FDEVP_CNX_ERROR
/* Endpoints of a connection have been changed (multihomed SCTP). */
,FDEVP_CNX_EP_CHANGE
/* The connection is being shutdown (SCTP notification). */
,FDEVP_CNX_SHUTDOWN
/* A new connection (with a CER) has been received */
,FDEVP_CNX_INCOMING
/* A new connection has been established to the remote peer (event data is the cnxctx object) */
,FDEVP_CNX_ESTABLISHED
/* A connection attempt (initiator side) has failed */
,FDEVP_CNX_FAILED
/* The PSM state is expired */
,FDEVP_PSM_TIMEOUT
};
#define CHECK_PEVENT( _e ) \
(((int)(_e) >= FDEVP_TERMINATE) && ((int)(_e) <= FDEVP_PSM_TIMEOUT))
/* The following macro is actually called in p_psm.c -- another solution would be to declare it static inline */
#define DECLARE_PEV_STR() \
const char * fd_pev_str(int event) \
{ \
switch (event) { \
case_str(FDEVP_TERMINATE); \
case_str(FDEVP_CNX_MSG_RECV); \
case_str(FDEVP_CNX_ERROR); \
case_str(FDEVP_CNX_EP_CHANGE); \
case_str(FDEVP_CNX_INCOMING); \
case_str(FDEVP_CNX_ESTABLISHED); \
case_str(FDEVP_CNX_FAILED); \
case_str(FDEVP_PSM_TIMEOUT); \
} \
TRACE_DEBUG(FULL, "Unknown event : %d", event); \
return "Unknown event"; \
}
const char * fd_pev_str(int event);
/* The data structure for FDEVP_CNX_INCOMING event */
struct cnx_incoming {
struct msg * cer; /* the CER message received on this connection */
struct cnxctx * cnx; /* The connection context */
int validate; /* The peer is new, it must be validated (by an extension) or error CEA to be sent */
};
/* Functions */
int fd_peer_fini();
int fd_peer_alloc(struct fd_peer ** ptr);
int fd_peer_free(struct fd_peer ** ptr);
int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx );
/* fd_peer_add declared in freeDiameter.h */
int fd_peer_validate( struct fd_peer * peer );
void fd_peer_failover_msg(struct fd_peer * peer);
/* Peer expiry */
int fd_p_expi_init(void);
int fd_p_expi_fini(void);
int fd_p_expi_update(struct fd_peer * peer );
/* Peer state machine */
int fd_psm_start();
int fd_psm_begin(struct fd_peer * peer );
int fd_psm_terminate(struct fd_peer * peer, char * reason );
void fd_psm_abord(struct fd_peer * peer );
void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay);
int fd_psm_change_state(struct fd_peer * peer, int new_state);
void fd_psm_cleanup(struct fd_peer * peer, int terminate);
/* Peer out */
int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, int update_reqin_cnt);
int fd_out_start(struct fd_peer * peer);
int fd_out_stop(struct fd_peer * peer);
/* Initiating connections */
int fd_p_cnx_init(struct fd_peer * peer);
void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all);
/* Peer sent requests cache */
int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore);
int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req);
int fd_p_sr_start(struct sr_list * srlist);
int fd_p_sr_stop(struct sr_list * srlist);
void fd_p_sr_failover(struct sr_list * srlist);
/* Local Link messages (CER/CEA, DWR/DWA, DPR/DPA) */
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_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, char * reason);
int fd_p_dp_newdelay(struct fd_peer * peer);
/* Active peers -- routing process should only ever take the read lock, the write lock is managed by PSMs */
extern struct fd_list fd_g_activ_peers;
extern pthread_rwlock_t fd_g_activ_peers_rw; /* protect the list */
/* Server sockets */
int fd_servers_start();
int fd_servers_stop();
/* Connection contexts -- there are also definitions in cnxctx.h for the relevant files */
struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep);
struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list);
int fd_cnx_serv_listen(struct cnxctx * conn);
struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv);
struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa, socklen_t addrlen);
struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list);
int fd_cnx_start_clear(struct cnxctx * conn, int loop);
void fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn);
int fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len);
#define ALGO_HANDSHAKE_DEFAULT 0 /* TLS for TCP, DTLS for SCTP */
#define ALGO_HANDSHAKE_3436 1 /* For TLS for SCTP also */
int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds);
char * fd_cnx_getid(struct cnxctx * conn);
int fd_cnx_getproto(struct cnxctx * conn);
int fd_cnx_getTLS(struct cnxctx * conn);
int fd_cnx_is_unordered_delivery_supported(struct cnxctx * conn);
int fd_cnx_unordered_delivery(struct cnxctx * conn, int is_allowed);
int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size);
int fd_cnx_get_local_eps(struct fd_list * list);
int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps);
char * fd_cnx_getremoteid(struct cnxctx * conn);
int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len);
int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len);
void fd_cnx_destroy(struct cnxctx * conn);
#ifdef GNUTLS_VERSION_300
int fd_tls_verify_credentials_2(gnutls_session_t session);
#endif /* GNUTLS_VERSION_300 */
/* Internal calls of the hook mechanism */
void fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl);
void fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl);
int fd_hooks_init(void);
size_t fd_msg_pmdl_sizewithoverhead(size_t datalen);
struct fd_msg_pmdl * fd_msg_pmdl_get_inbuf(uint8_t * buf, size_t datalen);
#endif /* _FDCORE_INTERNAL_H */
+285
View File
@@ -0,0 +1,285 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
/* Lex configuration parser.
*
* This file defines the token for parsing the daemon's configuration file
* Note that each extension has a separate independant configuration file.
*
* Note : This module is NOT thread-safe. All processing must be done from one thread only.
*/
%{
/* Include the daemon's header files */
#include "fdcore-internal.h"
/* Include yacc tokens definitions */
#include "fdd.tab.h"
/* Update the column information */
#ifdef DEBUG_LEX
#define YY_USER_ACTION { \
yylloc->first_column = yylloc->last_column + 1; \
yylloc->last_column = yylloc->first_column + yyleng - 1; \
fd_log_debug( \
"(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'", \
yylloc->first_line, yylloc->first_column, \
yylloc->last_line, yylloc->last_column, \
yy_act, yyleng, yytext); \
}
#else /* DEBUG_LEX */
#define YY_USER_ACTION { \
yylloc->first_column = yylloc->last_column + 1; \
yylloc->last_column = yylloc->first_column + yyleng - 1; \
}
#endif
/* %option noinput ? */
#define YY_NO_INPUT
/* Additional for files inclusion */
#include <glob.h>
#include <string.h>
#define MAX_NESTED_CONF_FILES 5
struct nested_conffiles_t {
YY_BUFFER_STATE parent_level_state;
glob_t filelist;
int current_file;
} nested_conffiles[MAX_NESTED_CONF_FILES];
int current_nested_level = 0;
int globerrfct(const char *epath, int eerrno)
{
TRACE_ERROR("Failed to scan %s: %s", epath, strerror(eerrno));
return 1;
}
%}
%option bison-bridge bison-locations
%option noyywrap
%option nounput
%x in_include
/* Quoted string. Multilines do not match. */
qstring \"[^\"\n]*\"
%%
<*>\n {
/* Update the line count */
yylloc->first_line++;
yylloc->last_line++;
yylloc->last_column=0;
}
<*>([[:space:]]{-}[\n])+ ; /* Eat all spaces, not new lines */
<*>#.*$ ; /* Eat all comments */
include BEGIN(in_include);
/* Following an "include" keyword */
<in_include>{
{qstring} { /* Name of the file to include. This is directly sent to glob. */
int globerror=0;
char * buf = strdup(yytext+1);
if (buf[yyleng-2] != '"')
{
TRACE_ERROR("Unterminated string: %s", yytext);
return LEX_ERROR;
}
buf[yyleng-2] = '\0';
if (current_nested_level >= MAX_NESTED_CONF_FILES)
{
TRACE_ERROR("Too many recursion levels in configuration files includes");
return LEX_ERROR;
}
/* glob the include */
globerror = glob(buf, GLOB_ERR, globerrfct, &nested_conffiles[current_nested_level].filelist);
if (globerror == GLOB_NOSPACE)
{
TRACE_ERROR("Not enough memory to parse include directive.");
return LEX_ERROR;
}
if (globerror == GLOB_ABORTED)
{
TRACE_ERROR("An error was encountered in include directive.");
return LEX_ERROR;
}
if (globerror == GLOB_NOMATCH)
{
globfree(&nested_conffiles[current_nested_level].filelist);
goto nomatch;
}
if (globerror)
{
TRACE_ERROR("Unexpected error in glob (%d).", globerror);
return LEX_ERROR;
}
/* We have a list of files to include. */
/* save the current buffer for returning when this include has been parsed */
nested_conffiles[current_nested_level].parent_level_state = YY_CURRENT_BUFFER;
/* Start with the first match */
nested_conffiles[current_nested_level].current_file = 0;
yyin = fopen( nested_conffiles[current_nested_level].filelist.gl_pathv[0], "r" );
if ( ! yyin )
{
TRACE_ERROR("Error in %s: %s", nested_conffiles[current_nested_level].filelist.gl_pathv[0], strerror(errno));
return LEX_ERROR;
}
yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ));
/* In case of recursive includes */
current_nested_level++;
nomatch:
BEGIN(INITIAL);
}
}
<<EOF>> {
if (current_nested_level == 0)
{
/* We are at the end of parsing */
yyterminate();
}
/* Otherwise we are doing an include statement */
--current_nested_level;
yy_delete_buffer(YY_CURRENT_BUFFER);
/* Go to next file, if any */
nested_conffiles[current_nested_level].current_file++;
if ( nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file] == NULL )
{
/* We have finished with this list of includes */
globfree(&nested_conffiles[current_nested_level].filelist);
yy_switch_to_buffer(nested_conffiles[current_nested_level].parent_level_state);
}
else
{
/* Proceed to next included file */
yyin = fopen( nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file], "r" );
if ( ! yyin )
{
TRACE_ERROR("Error in %s: %s", nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file], strerror(errno));
return LEX_ERROR;
}
yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ));
/* In case of recursive includes */
current_nested_level++;
}
}
{qstring} {
/* First copy the string without the quotes for use in the yacc parser */
CHECK_MALLOC_DO( yylval->string = strdup(yytext+1), /* This allocates one useless tail char but... it's easier :D */
return LEX_ERROR );/* on error, trig an error in yacc parser */
yylval->string[yyleng-2] = '\0';
/* the yacc parser will check the string is valid */
return QSTRING;
}
[[:digit:]]+ {
/* Convert this to an integer value */
int ret = sscanf(yytext, "%i", &yylval->integer);
if (ret != 1) {
/* No matching: an error occurred */
TRACE_ERROR("Unable to convert the value '%s' to a valid number: %s", yytext, strerror(errno));
return LEX_ERROR; /* trig an error in yacc parser */
/* Maybe we could REJECT instead of failing here? */
}
return INTEGER;
}
/* Full words tokens (keywords) */
(?i:"Identity") { return IDENTITY; }
(?i:"Realm") { return REALM; }
(?i:"Port") { return PORT; }
(?i:"SecPort") { return SECPORT; }
/* (?i:"SctpSec3436") { return SEC3436; } */
(?i:"No_IPv6") { return NOIP6; }
(?i:"No_IP") { return NOIP; }
(?i:"No_TCP") { return NOTCP; }
(?i:"No_SCTP") { return NOSCTP; }
(?i:"Prefer_TCP") { return PREFERTCP; }
(?i:"TLS_old_method") { return OLDTLS; }
(?i:"SCTP_streams") { return SCTPSTREAMS; }
(?i:"AppServThreads") { return APPSERVTHREADS;}
(?i:"ListenOn") { return LISTENON; }
(?i:"ThreadsPerServer") { return THRPERSRV; }
(?i:"TcTimer") { return TCTIMER; }
(?i:"TwTimer") { return TWTIMER; }
(?i:"NoRelay") { return NORELAY; }
(?i:"LoadExtension") { return LOADEXT; }
(?i:"ConnectPeer") { return CONNPEER; }
(?i:"ConnectTo") { return CONNTO; }
(?i:"No_TLS") { return NOTLS; }
(?i:"TLS_Cred") { return TLS_CRED; }
(?i:"TLS_CA") { return TLS_CA; }
(?i:"TLS_CRL") { return TLS_CRL; }
(?i:"TLS_Prio") { return TLS_PRIO; }
(?i:"TLS_DH_bits") { return TLS_DH_BITS; }
(?i:"TLS_DH_file") { return TLS_DH_FILE; }
/* Valid single characters for yyparse */
<*>[=,:;{}] { return yytext[0]; }
/* Unrecognized token */
<*>[[:alnum:]]+ | /* This rule is only useful to print a complete token in error messages */
/* Unrecognized character */
<*>. {
TRACE_ERROR("Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext);
return LEX_ERROR;
}
%%
+667
View File
@@ -0,0 +1,667 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2015, 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. *
*********************************************************************************************************/
/* Yacc configuration parser.
*
* This file defines the grammar of the configuration file.
* Note that each extension has a separate independant configuration file.
*
* Note : This module is NOT thread-safe. All processing must be done from one thread only.
*/
/* For development only : */
%debug
%error-verbose
%parse-param {struct fd_config * conf}
/* Keep track of location */
%locations
%pure-parser
%{
#include "fdcore-internal.h"
#include "fdd.tab.h" /* bug : bison does not define the YYLTYPE before including this bloc, so... */
/* The Lex parser prototype */
int fddlex(YYSTYPE *lvalp, YYLTYPE *llocp);
/* Function to report error */
void yyerror (YYLTYPE *ploc, struct fd_config * conf, char const *s)
{
if (ploc->first_line != ploc->last_line) {
TRACE_ERROR("%s:%d.%d-%d.%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
} else if (ploc->first_column != ploc->last_column) {
TRACE_ERROR("%s:%d.%d-%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_column, s);
} else {
TRACE_ERROR("%s:%d.%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, s);
}
}
int got_peer_noip = 0;
int got_peer_noipv6 = 0;
int got_peer_notcp = 0;
int got_peer_nosctp = 0;
struct peer_info fddpi;
%}
/* Values returned by lex for token */
%union {
char *string; /* The string is allocated by strdup in lex.*/
int integer; /* Store integer values */
}
/* In case of error in the lexical analysis */
%token LEX_ERROR
%token <string> QSTRING
%token <integer> INTEGER
%type <string> extconf
%token IDENTITY
%token REALM
%token PORT
%token SECPORT
%token SEC3436
%token NOIP
%token NOIP6
%token NOTCP
%token NOSCTP
%token PREFERTCP
%token OLDTLS
%token NOTLS
%token SCTPSTREAMS
%token APPSERVTHREADS
%token LISTENON
%token THRPERSRV
%token TCTIMER
%token TWTIMER
%token NORELAY
%token LOADEXT
%token CONNPEER
%token CONNTO
%token TLS_CRED
%token TLS_CA
%token TLS_CRL
%token TLS_PRIO
%token TLS_DH_BITS
%token TLS_DH_FILE
/* -------------------------------------- */
%%
/* The grammar definition - Sections blocs. */
conffile: /* Empty is OK -- for simplicity here, we reject in daemon later */
| conffile identity
| conffile realm
| conffile tctimer
| conffile twtimer
| conffile port
| conffile secport
| conffile sec3436
| conffile sctpstreams
| conffile listenon
| conffile thrpersrv
| conffile norelay
| conffile appservthreads
| conffile noip
| conffile noip6
| conffile notcp
| conffile nosctp
| conffile prefertcp
| conffile oldtls
| conffile loadext
| conffile connpeer
| conffile tls_cred
| conffile tls_ca
| conffile tls_crl
| conffile tls_prio
| conffile tls_dh
| conffile errors
{
yyerror(&yylloc, conf, "An error occurred while parsing the configuration file");
return EINVAL;
}
;
/* Lexical or syntax error */
errors: LEX_ERROR
| error
;
identity: IDENTITY '=' QSTRING ';'
{
conf->cnf_diamid = $3;
}
;
realm: REALM '=' QSTRING ';'
{
conf->cnf_diamrlm = $3;
}
;
tctimer: TCTIMER '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($3 > 0),
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
conf->cnf_timer_tc = (unsigned int)$3;
}
;
twtimer: TWTIMER '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($3 > 5),
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
conf->cnf_timer_tw = (unsigned int)$3;
}
;
port: PORT '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
conf->cnf_port = (uint16_t)$3;
}
;
secport: SECPORT '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
conf->cnf_port_tls = (uint16_t)$3;
}
;
sec3436: SEC3436 '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
conf->cnf_port_3436 = (uint16_t)$3;
}
;
sctpstreams: SCTPSTREAMS '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
conf->cnf_sctp_str = (uint16_t)$3;
}
;
listenon: LISTENON '=' QSTRING ';'
{
struct addrinfo hints, *ai;
int ret;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
ret = getaddrinfo($3, NULL, &hints, &ai);
if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF ), YYERROR );
freeaddrinfo(ai);
free($3);
}
;
thrpersrv: THRPERSRV '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($3 > 0),
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
conf->cnf_thr_srv = $3;
}
;
norelay: NORELAY ';'
{
conf->cnf_flags.no_fwd = 1;
}
;
appservthreads: APPSERVTHREADS '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($3 > 0) && ($3 < 256),
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
conf->cnf_dispthr = (uint16_t)$3;
}
;
noip: NOIP ';'
{
if (got_peer_noipv6) {
yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IPv6.");
YYERROR;
}
conf->cnf_flags.no_ip4 = 1;
}
;
noip6: NOIP6 ';'
{
if (got_peer_noip) {
yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IP.");
YYERROR;
}
conf->cnf_flags.no_ip6 = 1;
}
;
notcp: NOTCP ';'
{
#ifdef DISABLE_SCTP
yyerror (&yylloc, conf, "No_TCP cannot be specified for daemon compiled with DISABLE_SCTP option.");
YYERROR;
#endif
if (conf->cnf_flags.no_sctp)
{
yyerror (&yylloc, conf, "No_TCP conflicts with No_SCTP directive." );
YYERROR;
}
if (got_peer_nosctp) {
yyerror (&yylloc, conf, "No_TCP conflicts with a ConnectPeer directive No_SCTP.");
YYERROR;
}
conf->cnf_flags.no_tcp = 1;
}
;
nosctp: NOSCTP ';'
{
if (conf->cnf_flags.no_tcp)
{
yyerror (&yylloc, conf, "No_SCTP conflicts with No_TCP directive." );
YYERROR;
}
if (got_peer_notcp) {
yyerror (&yylloc, conf, "No_SCTP conflicts with a ConnectPeer directive No_TCP.");
YYERROR;
}
conf->cnf_flags.no_sctp = 1;
}
;
prefertcp: PREFERTCP ';'
{
conf->cnf_flags.pr_tcp = 1;
}
;
oldtls: OLDTLS ';'
{
conf->cnf_flags.tls_alg = 1;
}
;
loadext: LOADEXT '=' QSTRING extconf ';'
{
char * fname;
char * cfname;
FILE * fd;
/* Try and open the extension file */
fname = $3;
fd = fopen(fname, "r");
if ((fd == NULL) && (*fname != '/')) {
char * bkp = fname;
CHECK_MALLOC_DO( fname = malloc( strlen(bkp) + strlen(DEFAULT_EXTENSIONS_PATH) + 2 ),
{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
sprintf(fname, DEFAULT_EXTENSIONS_PATH "/%s", bkp);
fd = fopen(fname, "r");
if (fd == NULL) {
free(fname);
fname = bkp;
} else {
free(bkp);
}
}
if (fd != NULL) {
fclose(fd);
} /* otherwise, LD_LIBRARY_PATH will be tested by dl_open.
This should not give any security issue, otherwise we can add an "else fail" here. */
/* Try and open the configuration file (optional) */
cfname = $4;
if (cfname) {
fd = fopen(cfname, "r");
if ((fd == NULL) && (*cfname != '/')) {
char * test;
CHECK_MALLOC_DO( test = malloc( strlen(cfname) + strlen(DEFAULT_CONF_PATH) + 2 ),
{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
sprintf(test, DEFAULT_CONF_PATH "/%s", cfname);
fd = fopen(test, "r");
if (fd) {
free(cfname);
cfname=test;
} else {
/* This is not an error, we allow an extension to wait for something else than a real conf file. */
free(test);
}
}
if (fd)
fclose(fd);
}
CHECK_FCT_DO( fd_ext_add( fname, cfname ),
{ yyerror (&yylloc, conf, "Error adding extension"); YYERROR; } );
}
;
extconf: /* empty */
{
$$ = NULL;
}
| ':' QSTRING
{
$$ = $2;
}
;
connpeer: {
memset(&fddpi, 0, sizeof(fddpi));
fddpi.config.pic_flags.persist = PI_PRST_ALWAYS;
fd_list_init( &fddpi.pi_endpoints, NULL );
}
CONNPEER '=' QSTRING peerinfo ';'
{
fddpi.pi_diamid = $4;
CHECK_FCT_DO( fd_peer_add ( &fddpi, conf->cnf_file, NULL, NULL ),
{ yyerror (&yylloc, conf, "Error adding ConnectPeer information"); YYERROR; } );
/* Now destroy any content in the structure */
free(fddpi.pi_diamid);
free(fddpi.config.pic_realm);
free(fddpi.config.pic_priority);
while (!FD_IS_LIST_EMPTY(&fddpi.pi_endpoints)) {
struct fd_list * li = fddpi.pi_endpoints.next;
fd_list_unlink(li);
free(li);
}
}
;
peerinfo: /* empty */
| '{' peerparams '}'
;
peerparams: /* empty */
| peerparams NOIP ';'
{
if ((conf->cnf_flags.no_ip6) || (fddpi.config.pic_flags.pro3 == PI_P3_IP)) {
yyerror (&yylloc, conf, "No_IP conflicts with a No_IPv6 directive.");
YYERROR;
}
got_peer_noip++;
fddpi.config.pic_flags.pro3 = PI_P3_IPv6;
}
| peerparams NOIP6 ';'
{
if ((conf->cnf_flags.no_ip4) || (fddpi.config.pic_flags.pro3 == PI_P3_IPv6)) {
yyerror (&yylloc, conf, "No_IPv6 conflicts with a No_IP directive.");
YYERROR;
}
got_peer_noipv6++;
fddpi.config.pic_flags.pro3 = PI_P3_IP;
}
| peerparams NOTCP ';'
{
#ifdef DISABLE_SCTP
yyerror (&yylloc, conf, "No_TCP cannot be specified in daemon compiled with DISABLE_SCTP option.");
YYERROR;
#endif
if ((conf->cnf_flags.no_sctp) || (fddpi.config.pic_flags.pro4 == PI_P4_TCP)) {
yyerror (&yylloc, conf, "No_TCP conflicts with a No_SCTP directive.");
YYERROR;
}
got_peer_notcp++;
fddpi.config.pic_flags.pro4 = PI_P4_SCTP;
}
| peerparams NOSCTP ';'
{
if ((conf->cnf_flags.no_tcp) || (fddpi.config.pic_flags.pro4 == PI_P4_SCTP)) {
yyerror (&yylloc, conf, "No_SCTP conflicts with a No_TCP directive.");
YYERROR;
}
got_peer_nosctp++;
fddpi.config.pic_flags.pro4 = PI_P4_TCP;
}
| peerparams PREFERTCP ';'
{
fddpi.config.pic_flags.alg = PI_ALGPREF_TCP;
}
| peerparams OLDTLS ';'
{
fddpi.config.pic_flags.sec |= PI_SEC_TLS_OLD;
}
| peerparams NOTLS ';'
{
fddpi.config.pic_flags.sec |= PI_SEC_NONE;
}
| peerparams SEC3436 ';'
{
fddpi.config.pic_flags.sctpsec |= PI_SCTPSEC_3436;
}
| peerparams REALM '=' QSTRING ';'
{
fddpi.config.pic_realm = $4;
}
| peerparams PORT '=' INTEGER ';'
{
CHECK_PARAMS_DO( ($4 > 0) && ($4 < 1<<16),
{ yyerror (&yylloc, conf, "Invalid port value"); YYERROR; } );
fddpi.config.pic_port = (uint16_t)$4;
}
| peerparams TCTIMER '=' INTEGER ';'
{
fddpi.config.pic_tctimer = $4;
}
| peerparams TWTIMER '=' INTEGER ';'
{
fddpi.config.pic_twtimer = $4;
}
| peerparams TLS_PRIO '=' QSTRING ';'
{
fddpi.config.pic_priority = $4;
}
| peerparams CONNTO '=' QSTRING ';'
{
struct addrinfo hints, *ai;
int ret;
int disc = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
ret = getaddrinfo($4, NULL, &hints, &ai);
if (ret == EAI_NONAME) {
/* The name was maybe not numeric, try again */
disc = EP_FL_DISC;
hints.ai_flags &= ~ AI_NUMERICHOST;
ret = getaddrinfo($4, NULL, &hints, &ai);
}
if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF | (disc ?: EP_ACCEPTALL) ), YYERROR );
free($4);
freeaddrinfo(ai);
}
;
tls_cred: TLS_CRED '=' QSTRING ',' QSTRING ';'
{
FILE * fd;
fd = fopen($3, "r");
if (fd == NULL) {
int ret = errno;
TRACE_ERROR("Unable to open certificate file %s for reading: %s", $3, strerror(ret));
yyerror (&yylloc, conf, "Error on file name");
YYERROR;
}
fclose(fd);
fd = fopen($5, "r");
if (fd == NULL) {
int ret = errno;
TRACE_ERROR("Unable to open private key file %s for reading: %s", $5, strerror(ret));
yyerror (&yylloc, conf, "Error on file name");
YYERROR;
}
fclose(fd);
conf->cnf_sec_data.cert_file = $3;
conf->cnf_sec_data.key_file = $5;
CHECK_GNUTLS_DO( gnutls_certificate_set_x509_key_file(
conf->cnf_sec_data.credentials,
conf->cnf_sec_data.cert_file,
conf->cnf_sec_data.key_file,
GNUTLS_X509_FMT_PEM),
{ yyerror (&yylloc, conf, "Error opening certificate or private key file."); YYERROR; } );
}
;
tls_ca: TLS_CA '=' QSTRING ';'
{
FILE * fd;
fd = fopen($3, "rb");
if (fd == NULL) {
int ret = errno;
TRACE_ERROR("Unable to open CA file %s for reading: %s", $3, strerror(ret));
yyerror (&yylloc, conf, "Error on file name");
YYERROR;
}
#ifdef GNUTLS_VERSION_300
{
/* We import these CA in the trust list */
gnutls_x509_crt_t * calist;
unsigned int cacount;
gnutls_datum_t cafile;
CHECK_FCT_DO( fd_conf_stream_to_gnutls_datum(fd, &cafile),
{ yyerror (&yylloc, conf, "Error reading CA file."); YYERROR; } );
CHECK_GNUTLS_DO( gnutls_x509_crt_list_import2(&calist, &cacount, &cafile, GNUTLS_X509_FMT_PEM,
GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED),
{ yyerror (&yylloc, conf, "Error importing CA file."); YYERROR; } );
free(cafile.data);
CHECK_GNUTLS_DO( gnutls_x509_trust_list_add_cas (fd_g_config->cnf_sec_data.trustlist, calist, cacount, 0),
{ yyerror (&yylloc, conf, "Error saving CA in trust list."); YYERROR; } );
}
#endif /* GNUTLS_VERSION_300 */
fclose(fd);
conf->cnf_sec_data.ca_file = $3;
CHECK_GNUTLS_DO( conf->cnf_sec_data.ca_file_nr += gnutls_certificate_set_x509_trust_file(
conf->cnf_sec_data.credentials,
conf->cnf_sec_data.ca_file,
GNUTLS_X509_FMT_PEM),
{ yyerror (&yylloc, conf, "Error setting CA parameters."); YYERROR; } );
}
;
tls_crl: TLS_CRL '=' QSTRING ';'
{
FILE * fd;
fd = fopen($3, "rb");
if (fd == NULL) {
int ret = errno;
TRACE_ERROR("Unable to open CRL file %s for reading: %s", $3, strerror(ret));
yyerror (&yylloc, conf, "Error on file name");
YYERROR;
}
#ifdef GNUTLS_VERSION_300
{
/* We import these CRL in the trust list */
gnutls_x509_crl_t * crllist;
unsigned int crlcount;
gnutls_datum_t crlfile;
CHECK_FCT_DO( fd_conf_stream_to_gnutls_datum(fd, &crlfile),
{ yyerror (&yylloc, conf, "Error reading CRL file."); YYERROR; } );
CHECK_GNUTLS_DO( gnutls_x509_crl_list_import2(&crllist, &crlcount, &crlfile, GNUTLS_X509_FMT_PEM, 0),
{ yyerror (&yylloc, conf, "Error importing CRL file."); YYERROR; } );
free(crlfile.data);
CHECK_GNUTLS_DO( gnutls_x509_trust_list_add_crls (fd_g_config->cnf_sec_data.trustlist, crllist, crlcount,
GNUTLS_TL_VERIFY_CRL,
0),
{ yyerror (&yylloc, conf, "Error importing CRL in trust list."); YYERROR; } );
}
#endif /* GNUTLS_VERSION_300 */
fclose(fd);
conf->cnf_sec_data.crl_file = $3;
CHECK_GNUTLS_DO( gnutls_certificate_set_x509_crl_file(
conf->cnf_sec_data.credentials,
conf->cnf_sec_data.crl_file,
GNUTLS_X509_FMT_PEM),
{ yyerror (&yylloc, conf, "Error setting CRL parameters."); YYERROR; } );
}
;
tls_prio: TLS_PRIO '=' QSTRING ';'
{
const char * err_pos = NULL;
conf->cnf_sec_data.prio_string = $3;
CHECK_GNUTLS_DO( gnutls_priority_init(
&conf->cnf_sec_data.prio_cache,
conf->cnf_sec_data.prio_string,
&err_pos),
{ yyerror (&yylloc, conf, "Error setting Priority parameter.");
TRACE_ERROR("Error at position : %s", err_pos);
YYERROR; } );
}
;
tls_dh: TLS_DH_BITS '=' INTEGER ';'
{
conf->cnf_sec_data.dh_bits = $3;
}
| TLS_DH_FILE '=' QSTRING ';'
{
FILE * fd;
free(conf->cnf_sec_data.dh_file);
conf->cnf_sec_data.dh_file = $3;
fd = fopen($3, "r");
if (fd == NULL) {
int ret = errno;
TRACE_ERROR("Unable to open DH file %s for reading: %s", $3, strerror(ret));
yyerror (&yylloc, conf, "Error on file name");
YYERROR;
}
fclose(fd);
}
;
+79
View File
@@ -0,0 +1,79 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* See include/freeDiameter/libfdcore.h for more information */
int fd_stat_getstats(enum fd_stat_type stat, struct peer_hdr * peer,
int * current_count, int * limit_count, int * highest_count, long long * total_count,
struct timespec * total, struct timespec * blocking, struct timespec * last)
{
struct fd_peer * p = (struct fd_peer *)peer;
TRACE_ENTRY( "%d %p %p %p %p %p %p %p %p", stat, peer, current_count, limit_count, highest_count, total_count, total, blocking, last);
switch (stat) {
case STAT_G_LOCAL: {
CHECK_FCT( fd_fifo_getstats(fd_g_local, current_count, limit_count, highest_count, total_count, total, blocking, last) );
}
break;
case STAT_G_INCOMING: {
CHECK_FCT( fd_fifo_getstats(fd_g_incoming, current_count, limit_count, highest_count, total_count, total, blocking, last) );
}
break;
case STAT_G_OUTGOING: {
CHECK_FCT( fd_fifo_getstats(fd_g_outgoing, current_count, limit_count, highest_count, total_count, total, blocking, last) );
}
break;
case STAT_P_PSM: {
CHECK_PARAMS( CHECK_PEER( peer ) );
CHECK_FCT( fd_fifo_getstats(p->p_events, current_count, limit_count, highest_count, total_count, total, blocking, last) );
}
break;
case STAT_P_TOSEND: {
CHECK_PARAMS( CHECK_PEER( peer ) );
CHECK_FCT( fd_fifo_getstats(p->p_tosend, current_count, limit_count, highest_count, total_count, total, blocking, last) );
}
break;
default:
return EINVAL;
}
return 0;
}
+106
View File
@@ -0,0 +1,106 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
/* Configuration from compile-time */
#ifndef FD_IS_CONFIG
#define FD_IS_CONFIG
#ifdef __cplusplus
extern "C" {
#endif
/* #undef HAVE_NTOHLL */
#define HAVE_MALLOC_H
/* #undef HAVE_SIGNALENT_H */
#define HAVE_AI_ADDRCONFIG
#define HAVE_CLOCK_GETTIME
#define HAVE_STRNDUP
#define HAVE_PTHREAD_BAR
/* #undef HOST_BIG_ENDIAN */
/* #undef DISABLE_SCTP */
/* #undef DEBUG_SCTP */
/* #undef DEBUG_WITH_META */
/* #undef SCTP_USE_MAPPED_ADDRESSES */
#define SCTP_CONNECTX_4_ARGS
/* #undef SKIP_DLCLOSE */
/* #undef DIAMID_IDNA_IGNORE */
/* #undef DIAMID_IDNA_REJECT */
/* #undef DISABLE_PEER_EXPIRY */
/* #undef WORKAROUND_ACCEPT_INVALID_VSAI */
#define GNUTLS_VERSION_210
#define GNUTLS_VERSION_212
/* #undef GNUTLS_VERSION_300 */
/* #undef GNUTLS_VERSION_310 */
/* #undef ERRORS_ON_TODO */
/* #undef DEBUG */
#define FD_PROJECT_BINARY "freeDiameterd"
#define FD_PROJECT_NAME "freeDiameter"
#define FD_PROJECT_VERSION_MAJOR 1
#ifndef FD_PROJECT_VERSION_MAJOR
# define FD_PROJECT_VERSION_MAJOR 0
#endif /*FD_PROJECT_VERSION_MAJOR*/
#define FD_PROJECT_VERSION_MINOR 2
#ifndef FD_PROJECT_VERSION_MINOR
# define FD_PROJECT_VERSION_MINOR 0
#endif /*FD_PROJECT_VERSION_MINOR*/
#define FD_PROJECT_VERSION_REV 1
#ifndef FD_PROJECT_VERSION_REV
# define FD_PROJECT_VERSION_REV 0
#endif /*FD_PROJECT_VERSION_REV*/
#define FD_PROJECT_VERSION_API 6
#ifndef FD_PROJECT_VERSION_API
# define FD_PROJECT_VERSION_API 0
#endif /*FD_PROJECT_VERSION_API*/
#define FD_PROJECT_COPYRIGHT "Copyright (c) 2008-2015, WIDE Project (www.wide.ad.jp) and NICT (www.nict.go.jp)"
#define DEFAULT_CONF_PATH "DEFAULT_CONF_PATH"
#define DEFAULT_EXTENSIONS_PATH "CMAKE_INSTALL_PREFIX/INSTALL_EXTENSIONS_SUFFIX"
#ifndef FD_DEFAULT_CONF_FILENAME
#define FD_DEFAULT_CONF_FILENAME "freeDiameter.conf"
#endif /* FD_DEFAULT_CONF_FILENAME */
/* Maximum number of hooks handlers that can be registered. Make this compilation option if needed */
#define FD_HOOK_HANDLE_LIMIT 5
#ifdef __cplusplus
}
#endif
#endif /* FD_IS_CONFIG */
+454
View File
@@ -0,0 +1,454 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* Structures for the fd_hook_data_hdl management */
static struct fd_hook_data_hdl {
size_t pmd_size;
void (*pmd_init_cb)(struct fd_hook_permsgdata *);
void (*pmd_fini_cb)(struct fd_hook_permsgdata *);
} HDH_array[FD_HOOK_HANDLE_LIMIT];
static int max_index = 0;
static pthread_mutex_t HDH_lock = PTHREAD_MUTEX_INITIALIZER;
/* The structure linked from the msg structure list */
struct pmd_list_item {
struct fd_list chain; /* this list is ordered by hdl */
struct fd_hook_data_hdl * hdl;
struct fd_hook_permsgdata { } pmd; /* this data belongs to the extension; we only know the size of it */
};
#define sizeof_pmd(hdl) (((size_t)&((struct pmd_list_item *)0)->pmd) + hdl->pmd_size)
/* Now a hook registered by an extension */
struct fd_hook_hdl {
struct fd_list chain[HOOK_LAST+1];
void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata);
void *regdata;
struct fd_hook_data_hdl *data_hdl;
};
/* Array of those hooks */
struct {
struct fd_list sentinel;
pthread_rwlock_t rwlock;
} HS_array[HOOK_LAST+1];
/* Initialize the array of sentinels for the hooks */
int fd_hooks_init(void)
{
int i;
for (i=0; i <= HOOK_LAST; i++) {
fd_list_init(&HS_array[i].sentinel, NULL);
CHECK_POSIX( pthread_rwlock_init(&HS_array[i].rwlock, NULL) );
}
return 0;
}
/* Get a slot in the array */
int fd_hook_data_register(
size_t permsgdata_size,
void (*permsgdata_init_cb) (struct fd_hook_permsgdata *),
void (*permsgdata_fini_cb) (struct fd_hook_permsgdata *),
struct fd_hook_data_hdl **new_handle)
{
int ret = ENOSPC, idx;
TRACE_ENTRY("%zd %p %p %p", permsgdata_size, permsgdata_init_cb, permsgdata_fini_cb, new_handle);
CHECK_PARAMS( permsgdata_size && new_handle );
CHECK_POSIX( pthread_mutex_lock(&HDH_lock) );
if (max_index < FD_HOOK_HANDLE_LIMIT) {
idx = max_index++;
ret = 0;
}
CHECK_POSIX( pthread_mutex_unlock(&HDH_lock) );
if (ret == 0) {
HDH_array[idx].pmd_size = permsgdata_size;
HDH_array[idx].pmd_init_cb = permsgdata_init_cb;
HDH_array[idx].pmd_fini_cb = permsgdata_fini_cb;
*new_handle = &HDH_array[idx];
}
return ret;
}
/* Register a new hook callback */
int fd_hook_register ( uint32_t type_mask,
void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata),
void *regdata,
struct fd_hook_data_hdl *data_hdl,
struct fd_hook_hdl ** handler )
{
struct fd_hook_hdl * newhdl = NULL;
int i;
TRACE_ENTRY("%x %p %p %p %p", type_mask, fd_hook_cb, regdata, data_hdl, handler);
CHECK_PARAMS( fd_hook_cb && handler );
CHECK_MALLOC( newhdl = malloc(sizeof(struct fd_hook_hdl)) );
memset(newhdl, 0, sizeof(struct fd_hook_hdl));
newhdl->fd_hook_cb = fd_hook_cb;
newhdl->regdata = regdata;
newhdl->data_hdl = data_hdl;
for (i=0; i <= HOOK_LAST; i++) {
fd_list_init(&newhdl->chain[i], newhdl);
if (type_mask & (1<<i)) {
CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
fd_list_insert_before( &HS_array[i].sentinel, &newhdl->chain[i]);
CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
}
}
*handler = newhdl;
return 0;
}
/* free this hook callback */
int fd_hook_unregister( struct fd_hook_hdl * handler )
{
int i;
TRACE_ENTRY("%p", handler);
CHECK_PARAMS( handler );
for (i=0; i <= HOOK_LAST; i++) {
if ( ! FD_IS_LIST_EMPTY(&handler->chain[i])) {
CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
fd_list_unlink(&handler->chain[i]);
CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
}
}
free(handler);
return 0;
}
/* callback for the libfdproto to free the data associated with a message */
static void pmdl_free(struct fd_msg_pmdl *pmdl)
{
/* destroy all the items in the list */
while (!FD_IS_LIST_EMPTY(&pmdl->sentinel)) {
struct pmd_list_item * li = (struct pmd_list_item *)(pmdl->sentinel.next);
if (li->hdl->pmd_fini_cb) {
(*li->hdl->pmd_fini_cb)(&li->pmd);
}
fd_list_unlink(&li->chain);
free(li);
}
CHECK_POSIX_DO( pthread_mutex_destroy(&pmdl->lock), );
pmdl->sentinel.o = NULL;
}
/* Save the list of pmd into the message structure, as well as the callback to free this list */
void fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl)
{
struct fd_msg_pmdl * in_msg;
CHECK_PARAMS_DO( msg && pmdl, return );
in_msg = fd_msg_pmdl_get(msg);
ASSERT(in_msg && (in_msg->sentinel.o == NULL)); /* error / already initialized ??? */
in_msg->sentinel.o = pmdl_free;
/* Now move all items from the pmdl pointer into the initialized list */
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
fd_list_move_end(&in_msg->sentinel, &pmdl->sentinel);
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
pmdl_free(pmdl);
/* We're done */
}
/* Return the location of the permsgdata area corresponding to this handle, after eventually having created it. Return NULL in case of failure */
static struct fd_hook_permsgdata * get_or_create_pmd(struct fd_msg_pmdl *pmdl, struct fd_hook_hdl * h)
{
struct fd_hook_permsgdata * ret = NULL;
struct fd_list * li;
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
if (pmdl->sentinel.o == NULL) {
pmdl->sentinel.o = pmdl_free;
}
/* Search in the list for an item with the same handle. The list is ordered by this handle */
for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
struct pmd_list_item * pli = (struct pmd_list_item *) li;
if (pli->hdl == h->data_hdl)
ret = &pli->pmd;
if (pli->hdl >= h->data_hdl)
break;
}
if (!ret) {
/* we need to create a new one and insert before li */
struct pmd_list_item * pli;
CHECK_MALLOC_DO( pli = malloc(sizeof_pmd(h->data_hdl)), );
if (pli) {
memset(pli, 0, sizeof_pmd(h->data_hdl));
fd_list_init(&pli->chain, pli);
pli->hdl = h->data_hdl;
ret = &pli->pmd;
if (h->data_hdl->pmd_init_cb) {
(*h->data_hdl->pmd_init_cb)(ret);
}
fd_list_insert_before(li, &pli->chain);
}
}
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
return ret;
}
struct fd_hook_permsgdata * fd_hook_get_request_pmd(struct fd_hook_data_hdl *data_hdl, struct msg * answer)
{
struct msg * qry;
struct fd_msg_pmdl *pmdl;
struct fd_hook_permsgdata * ret = NULL;
struct fd_list * li;
CHECK_FCT_DO( fd_msg_answ_getq(answer, &qry), return NULL );
if (!qry)
return NULL;
pmdl = fd_msg_pmdl_get(qry);
if (!pmdl)
return NULL;
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
/* Search in the list for an item with the same handle. The list is ordered by this handle */
for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
struct pmd_list_item * pli = (struct pmd_list_item *) li;
if (pli->hdl == data_hdl)
ret = &pli->pmd;
if (pli->hdl >= data_hdl)
break;
}
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
return ret;
}
/* Create a mask */
uint32_t fd_hook_mask_helper(int dummy, ...)
{
va_list ap;
uint32_t ret = 0;
int next;
va_start(ap, dummy);
while ((next = va_arg(ap, int)) >= 0) {
if (next > HOOK_LAST)
break; /* invalid parameter */
ret |= (1<<next);
}
va_end(ap);
return ret;
}
static pthread_mutex_t hook_default_mtx = PTHREAD_MUTEX_INITIALIZER;
static char * hook_default_buf = NULL;
static size_t hook_default_len = 0;
/* The function that does the work of calling the extension's callbacks and also managing the permessagedata structures */
void fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl)
{
struct fd_list * li;
ASSERT(type <= HOOK_LAST);
int call_default = 0;
/* lock the list of hooks for this type */
CHECK_POSIX_DO( pthread_rwlock_rdlock(&HS_array[type].rwlock), );
pthread_cleanup_push( fd_cleanup_rwlock, &HS_array[type].rwlock );
if (FD_IS_LIST_EMPTY(&HS_array[type].sentinel)) {
call_default = 1;
} else {
/* for each registered hook */
for (li = HS_array[type].sentinel.next; li != &HS_array[type].sentinel; li = li->next) {
struct fd_hook_hdl * h = (struct fd_hook_hdl *)li->o;
struct fd_hook_permsgdata * pmd = NULL;
/* do we need to handle pmd ? */
if (h->data_hdl && pmdl) {
pmd = get_or_create_pmd(pmdl, h);
}
/* Now, call this callback */
(*h->fd_hook_cb)(type, msg, &peer->p_hdr, other, pmd, h->regdata);
}
}
pthread_cleanup_pop(0);
/* done */
CHECK_POSIX_DO( pthread_rwlock_unlock(&HS_array[type].rwlock), );
if (call_default) {
CHECK_POSIX_DO( pthread_mutex_lock(&hook_default_mtx), );
pthread_cleanup_push( fd_cleanup_mutex, &hook_default_mtx );
/* There was no registered handler, default behavior for this hook */
switch (type) {
case HOOK_DATA_RECEIVED: {
struct fd_cnx_rcvdata *rcv_data = other;
LOG_A("RCV: %zd bytes", rcv_data->length);
break;
}
case HOOK_MESSAGE_RECEIVED: {
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_D("RCV from '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
break;
}
case HOOK_MESSAGE_LOCAL: {
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_A("Handled to framework for sending: %s", hook_default_buf);
break;
}
case HOOK_MESSAGE_SENDING: {
LOG_A("SENDING message to '%s'", peer ? peer->p_hdr.info.pi_diamid : "<unknown>");
break;
}
case HOOK_MESSAGE_SENT: {
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_D("SENT to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
break;
}
case HOOK_MESSAGE_FAILOVER: {
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_D("Failing over message sent to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
break;
}
case HOOK_MESSAGE_PARSING_ERROR: {
if (msg) {
DiamId_t id = NULL;
if (!fd_msg_source_get( msg, &id, NULL ))
id = (DiamId_t)"<error getting source>";
if (!id)
id = (DiamId_t)"<local>";
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_E("Parsing error: '%s' for the following message received from '%s':", (char *)other, (char *)id);
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
} else {
struct fd_cnx_rcvdata *rcv_data = other;
CHECK_MALLOC_DO(fd_dump_extend_hexdump(&hook_default_buf, &hook_default_len, NULL, rcv_data->buffer, rcv_data->length, 0, 0), break);
LOG_E("Parsing error: cannot parse %zdB buffer from '%s': %s", rcv_data->length, peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
}
break;
}
case HOOK_MESSAGE_PARSING_ERROR2: {
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_E("Returning following message after parsing error:");
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
break;
}
case HOOK_MESSAGE_ROUTING_ERROR: {
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_E("Routing error: '%s' for the following message:", (char *)other);
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
break;
}
case HOOK_MESSAGE_ROUTING_FORWARD: {
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_D("FORWARDING: %s", hook_default_buf);
break;
}
case HOOK_MESSAGE_ROUTING_LOCAL: {
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_D("DISPATCHING: %s", hook_default_buf);
break;
}
case HOOK_MESSAGE_DROPPED: {
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_E("Message discarded ('%s'):", (char *)other);
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
break;
}
case HOOK_PEER_CONNECT_FAILED: {
if (msg) {
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
LOG_N("Connection to '%s' failed: '%s'; CER/CEA dump:", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
LOG_SPLIT(FD_LOG_NOTICE, " ", hook_default_buf, NULL);
} else {
LOG_D("Connection to '%s' failed: %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
}
break;
}
case HOOK_PEER_CONNECT_SUCCESS: {
DiamId_t id = NULL;
if ((!fd_msg_source_get( msg, &id, NULL )) && (id == NULL)) { /* The CEA is locally issued */
fd_msg_answ_getq(msg, &msg); /* We dump the CER in that case */
}
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
char protobuf[40];
if (peer) {
CHECK_FCT_DO(fd_peer_cnx_proto_info(&peer->p_hdr, protobuf, sizeof(protobuf)), break );
} else {
protobuf[0] = '-';
protobuf[1] = '\0';
}
LOG_N("Connected to '%s' (%s), remote capabilities: ", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", protobuf);
LOG_SPLIT(FD_LOG_NOTICE, " ", hook_default_buf, NULL);
break;
}
}
pthread_cleanup_pop(0);
CHECK_POSIX_DO( pthread_mutex_unlock(&hook_default_mtx), );
}
}
+452
View File
@@ -0,0 +1,452 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2015, 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 "fdcore-internal.h"
static struct dict_object * dict_avp_SI = NULL; /* Session-Id */
static struct dict_object * dict_avp_OH = NULL; /* Origin-Host */
static struct dict_object * dict_avp_OR = NULL; /* Origin-Realm */
static struct dict_object * dict_avp_EM = NULL; /* Error-Message */
static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */
static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */
static struct dict_object * dict_avp_RC = NULL; /* Result-Code */
struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
struct dict_object * fd_dict_avp_DC = NULL; /* Disconnect-Cause */
struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */
/* Resolve the dictionary objects */
int fd_msg_init(void)
{
TRACE_ENTRY("");
/* Initialize the dictionary objects that we may use frequently */
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &dict_avp_SI , ENOENT) );
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &dict_avp_OH , ENOENT) );
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &dict_avp_OR , ENOENT) );
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", &fd_dict_avp_OSI , ENOENT) );
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &dict_avp_RC , ENOENT) );
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &dict_avp_EM , ENOENT) );
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT) );
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &dict_avp_FAVP, ENOENT) );
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", &fd_dict_avp_DC , ENOENT) );
CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) );
CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) );
CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) );
return 0;
}
/* Add Origin-Host, Origin-Realm, Origin-State-Id AVPS at the end of the message */
int fd_msg_add_origin ( struct msg * msg, int osi )
{
union avp_value val;
struct avp * avp_OH = NULL;
struct avp * avp_OR = NULL;
struct avp * avp_OSI = NULL;
TRACE_ENTRY("%p", msg);
CHECK_PARAMS( msg );
/* Create the Origin-Host AVP */
CHECK_FCT( fd_msg_avp_new( dict_avp_OH, 0, &avp_OH ) );
/* Set its value */
memset(&val, 0, sizeof(val));
val.os.data = (os0_t)fd_g_config->cnf_diamid;
val.os.len = fd_g_config->cnf_diamid_len;
CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) );
/* Add it to the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OH ) );
/* Create the Origin-Realm AVP */
CHECK_FCT( fd_msg_avp_new( dict_avp_OR, 0, &avp_OR ) );
/* Set its value */
memset(&val, 0, sizeof(val));
val.os.data = (os0_t)fd_g_config->cnf_diamrlm;
val.os.len = fd_g_config->cnf_diamrlm_len;
CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) );
/* Add it to the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OR ) );
if (osi) {
/* Create the Origin-State-Id AVP */
CHECK_FCT( fd_msg_avp_new( fd_dict_avp_OSI, 0, &avp_OSI ) );
/* Set its value */
memset(&val, 0, sizeof(val));
val.u32 = fd_g_config->cnf_orstateid;
CHECK_FCT( fd_msg_avp_setvalue( avp_OSI, &val ) );
/* Add it to the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) );
}
return 0;
}
/* Create a new Session-Id and add at the beginning of the message. */
int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen )
{
union avp_value val;
struct avp * avp = NULL;
struct session * sess = NULL;
os0_t sid;
size_t sidlen;
TRACE_ENTRY("%p %p %zd", msg, opt, optlen);
CHECK_PARAMS( msg );
/* Check there is not already a session in the message */
CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) );
CHECK_PARAMS( sess == NULL );
/* Ok, now create the session */
CHECK_FCT( fd_sess_new ( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, opt, optlen ) );
CHECK_FCT( fd_sess_getsid( sess, &sid, &sidlen) );
/* Create an AVP to hold it */
CHECK_FCT( fd_msg_avp_new( dict_avp_SI, 0, &avp ) );
/* Set its value */
memset(&val, 0, sizeof(val));
val.os.data = sid;
val.os.len = sidlen;
CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
/* Add it to the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_FIRST_CHILD, avp ) );
/* Save the session associated with the message */
CHECK_FCT( fd_msg_sess_set( msg, sess) );
/* Done! */
return 0;
}
/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
{
union avp_value val;
struct avp * avp_RC = NULL;
struct avp * avp_EM = NULL;
struct avp * avp_ERH = NULL;
struct avp * avp_FAVP= NULL;
uint32_t rc_val = 0;
int set_e_bit=0;
int std_err_msg=0;
TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id);
CHECK_PARAMS( msg && rescode );
/* Find the enum value corresponding to the rescode string, this will give the class of error */
{
struct dict_object * enum_obj = NULL;
struct dict_enumval_request req;
memset(&req, 0, sizeof(struct dict_enumval_request));
/* First, get the enumerated type of the Result-Code AVP (this is fast, no need to cache the object) */
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT ) );
/* Now search for the value given as parameter */
req.search.enum_name = rescode;
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP) );
/* finally retrieve its data */
CHECK_FCT_DO( fd_dict_getval( enum_obj, &(req.search) ), return EINVAL );
/* copy the found value, we're done */
rc_val = req.search.enum_value.u32;
}
if (type_id == 1) {
/* Add the Origin-Host and Origin-Realm AVP */
CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
}
/* Create the Result-Code AVP */
CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
/* Set its value */
memset(&val, 0, sizeof(val));
val.u32 = rc_val;
CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
/* Add it to the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
if (type_id == 2) {
/* Add the Error-Reporting-Host AVP */
CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
/* Set its value */
memset(&val, 0, sizeof(val));
val.os.data = (uint8_t *)fd_g_config->cnf_diamid;
val.os.len = fd_g_config->cnf_diamid_len;
CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
/* Add it to the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
}
/* Now add the optavp in a FailedAVP if provided */
if (optavp) {
struct avp * optavp_cpy = NULL;
struct avp_hdr *opt_hdr, *optcpy_hdr;
struct dict_object * opt_model = NULL;
int is_grouped = 0;
/* Create the Failed-AVP AVP */
CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) );
/* Was this AVP a grouped one? Best effort only here */
if (!fd_msg_model ( optavp, &opt_model ) && (opt_model != NULL)) {
struct dict_avp_data dictdata;
CHECK_FCT( fd_dict_getval(opt_model, &dictdata) );
if (dictdata.avp_basetype == AVP_TYPE_GROUPED)
is_grouped = 1;
}
/* Create a new AVP with a copy of the data of the invalid or missing AVP */
optavp_cpy = optavp;
if (is_grouped) {
CHECK_FCT( fd_msg_avp_new( opt_model, 0, &optavp_cpy) );
} else {
CHECK_FCT( fd_msg_avp_new( NULL, AVPFL_SET_BLANK_VALUE | AVPFL_SET_RAWDATA_FROM_AVP, &optavp_cpy) );
CHECK_FCT( fd_msg_avp_hdr(optavp, &opt_hdr) );
CHECK_FCT( fd_msg_avp_hdr(optavp_cpy, &optcpy_hdr) );
memcpy(optcpy_hdr, opt_hdr, sizeof(struct avp_hdr));
}
/* Add the passed AVP inside it */
CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp_cpy ) );
/* And add to the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) );
}
/* Deal with the 'E' bit and the error message */
switch (rc_val / 1000) {
case 1: /* Informational */
case 2: /* Success */
/* Nothing special here: no E bit, no error message unless one is specified */
break;
case 3: /* Protocol Errors */
set_e_bit = 1;
std_err_msg = 1;
break;
case 4: /* Transcient Failure */
case 5: /* Permanent Failure */
default:
std_err_msg = 1;
break;
}
{
struct msg_hdr * hdr = NULL;
CHECK_FCT( fd_msg_hdr( msg, &hdr ) );
if (set_e_bit)
hdr->msg_flags |= CMD_FLAG_ERROR;
else
hdr->msg_flags &= ~ CMD_FLAG_ERROR;
}
if (std_err_msg || errormsg) {
/* Add the Error-Message AVP */
CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
/* Set its value */
memset(&val, 0, sizeof(val));
if (errormsg) {
val.os.data = (uint8_t *)errormsg;
val.os.len = strlen(errormsg);
} else {
val.os.data = (uint8_t *)rescode;
val.os.len = strlen(rescode);
}
CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
/* Add it to the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) );
}
return 0;
}
static int fd_msg_send_int( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
{
struct msg_hdr *hdr;
DiamId_t diamid;
/* Save the callback in the message, with the timeout */
CHECK_FCT( fd_msg_anscb_associate( *pmsg, anscb, data, expirecb, timeout ) );
/* If this is a new request, call the HOOK_MESSAGE_LOCAL hook */
if ( (fd_msg_hdr(*pmsg, &hdr) == 0)
&& (hdr->msg_flags & CMD_FLAG_REQUEST)
&& (fd_msg_source_get(*pmsg, &diamid, NULL) == 0)
&& (diamid == NULL)) {
fd_hook_call(HOOK_MESSAGE_LOCAL, *pmsg, NULL, NULL, fd_msg_pmdl_get(*pmsg));
}
/* Post the message in the outgoing queue */
CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
return 0;
}
/* Send a message and optionally register a callback for an answer */
int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data )
{
TRACE_ENTRY("%p %p %p", pmsg, anscb, data);
CHECK_PARAMS( pmsg );
return fd_msg_send_int(pmsg, anscb, data, NULL, NULL);
}
/* The variation of the same function with a timeout callback */
int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
{
TRACE_ENTRY("%p %p %p %p %p", pmsg, anscb, data, expirecb, timeout);
CHECK_PARAMS( pmsg && expirecb && timeout );
return fd_msg_send_int(pmsg, anscb, data, expirecb, timeout);
}
/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */
int fd_msg_parse_or_error( struct msg ** msg, struct msg **error)
{
int ret = 0;
struct msg * m;
struct msg_hdr * hdr = NULL;
struct fd_pei pei;
TRACE_ENTRY("%p", msg);
CHECK_PARAMS(msg && *msg && error);
m = *msg;
*error = NULL;
/* Parse the message against our dictionary */
ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
if ((ret != EBADMSG) /* Parsing grouped AVP failed / Conflicting rule found */
&& (ret != ENOTSUP)) /* Command is not supported / Mandatory AVP is not supported */
return ret; /* 0 or another error */
/* Log */
fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, m, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(m));
CHECK_FCT( fd_msg_hdr(m, &hdr) );
/* Now create an answer error if the message is a query */
if (hdr->msg_flags & CMD_FLAG_REQUEST) {
/* Create the error message */
CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &m, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
/* Set the error code */
CHECK_FCT( fd_msg_rescode_set(m, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
/* free the pei AVP to avoid memory leak */
if (pei.pei_avp_free) {
fd_msg_free(pei.pei_avp);
}
*msg = NULL;
*error = m;
} else {
do { /* Rescue error messages */
struct avp * avp;
union avp_value * rc = NULL;
/* Search the Result-Code AVP */
CHECK_FCT_DO( fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break );
while (avp) {
struct avp_hdr * ahdr;
CHECK_FCT_DO( fd_msg_avp_hdr( avp, &ahdr ), break );
if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
/* Parse this AVP */
ASSERT( ahdr->avp_value );
rc = ahdr->avp_value;
break;
}
/* Go to next AVP */
CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break );
}
if (rc) {
switch (rc->u32 / 1000) {
case 1: /* 1xxx : Informational */
case 2: /* 2xxx : Sucess */
/* In these cases, we want the message to validate the ABNF, so we will discard the bad message */
break;
default: /* Other errors */
/* We let the application decide what to do with the message, we rescue it */
*error = m;
}
}
} while (0);
}
return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */
}
File diff suppressed because it is too large. Load diff
+357
View File
@@ -0,0 +1,357 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* TODO: change the behavior to handle properly forced ordering at beginning & end of OPEN state */
/* This file contains code used by a peer state machine to initiate a connection to remote peer */
struct next_conn {
struct fd_list chain;
int proto; /* Protocol of the next attempt */
union {
sSS ss; /* The address, only for TCP */
sSA4 sin;
sSA6 sin6;
};
uint16_t port; /* The port, for SCTP (included in ss for TCP) */
int dotls; /* Handshake TLS after connection ? */
};
static __inline__ void failed_connection_attempt(struct fd_peer * peer)
{
/* Simply remove the first item in the list if not empty */
if (! FD_IS_LIST_EMPTY(&peer->p_connparams) ) {
struct fd_list * li = peer->p_connparams.next;
fd_list_unlink(li);
free(li);
}
}
static void empty_connection_list(struct fd_peer * peer)
{
/* Remove all items */
while (!FD_IS_LIST_EMPTY(&peer->p_connparams)) {
failed_connection_attempt(peer);
}
}
static int prepare_connection_list(struct fd_peer * peer)
{
struct fd_list * li, *last_prio;
struct next_conn * new;
uint16_t port_no; /* network order */
int dotls_immediate;
int count = 0;
TRACE_ENTRY("%p", peer);
/* Resolve peer address(es) if needed */
if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
struct addrinfo hints, *ai, *aip;
int ret;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG;
ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai);
if (ret) {
TRACE_DEBUG(INFO, "Unable to resolve address for peer '%s' (%s), aborting", peer->p_hdr.info.pi_diamid, gai_strerror(ret));
if (ret != EAI_AGAIN)
fd_psm_terminate( peer, NULL );
return 0;
}
for (aip = ai; aip != NULL; aip = aip->ai_next) {
CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC ) );
}
freeaddrinfo(ai);
}
/* Remove addresses from unwanted family */
if (peer->p_hdr.info.config.pic_flags.pro3) {
CHECK_FCT( fd_ep_filter_family(
&peer->p_hdr.info.pi_endpoints,
(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ?
AF_INET
: AF_INET6));
}
if (fd_g_config->cnf_flags.no_ip4) {
CHECK_FCT( fd_ep_filter_family(
&peer->p_hdr.info.pi_endpoints,
AF_INET6));
}
if (fd_g_config->cnf_flags.no_ip6) {
CHECK_FCT( fd_ep_filter_family(
&peer->p_hdr.info.pi_endpoints,
AF_INET));
}
/* We don't use the alternate addresses that were sent by the remote peer */
CHECK_FCT( fd_ep_clearflags(&peer->p_hdr.info.pi_endpoints, EP_FL_ADV) );
/* Now check we have at least one address to attempt */
if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
TRACE_DEBUG(INFO, "No address %savailable to connect to peer '%s', aborting",
peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid);
fd_psm_terminate( peer, NULL );
return 0;
}
/* Check if we are able to communicate with this peer */
if (fd_g_config->cnf_sec_data.tls_disabled && ( peer->p_hdr.info.config.pic_flags.sec != PI_SEC_NONE)) {
LOG_E("Peer '%s' not configured for No_TLS and TLS is locally disabled; giving up connection attempts",
peer->p_hdr.info.pi_diamid);
fd_psm_terminate( peer, NULL );
return 0;
}
/* Cleanup any previous list */
empty_connection_list(peer);
/* Prepare the parameters */
if ((peer->p_hdr.info.config.pic_flags.sec != PI_SEC_DEFAULT) || (fd_g_config->cnf_flags.tls_alg)) {
dotls_immediate = 0;
port_no = htons(peer->p_hdr.info.config.pic_port ?: DIAMETER_PORT);
} else {
dotls_immediate = 1;
port_no = htons(peer->p_hdr.info.config.pic_port ?: DIAMETER_SECURE_PORT);
}
last_prio = &peer->p_connparams;
/* Create TCP parameters unless specified otherwise */
if ((!fd_g_config->cnf_flags.no_tcp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_SCTP)) {
for (li = peer->p_hdr.info.pi_endpoints.next; li != &peer->p_hdr.info.pi_endpoints; li = li->next) {
struct fd_endpoint * ep = (struct fd_endpoint *)li;
CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
memset(new, 0, sizeof(struct next_conn));
fd_list_init(&new->chain, new);
new->proto = IPPROTO_TCP;
memcpy( &new->ss, &ep->ss, sizeof(sSS) );
switch (new->ss.ss_family) {
case AF_INET:
new->sin.sin_port = port_no;
break;
case AF_INET6:
new->sin6.sin6_port = port_no;
break;
default:
free(new);
continue; /* Move to the next endpoint */
}
new->dotls = dotls_immediate;
/* Add the new entry to the appropriate position (conf and disc go first) */
if (ep->flags & (EP_FL_CONF | EP_FL_DISC)) {
fd_list_insert_after(last_prio, &new->chain);
last_prio = &new->chain;
} else {
fd_list_insert_before(&peer->p_connparams, &new->chain);
}
count++;
}
}
/* Now, add the SCTP entry, if not disabled */
#ifndef DISABLE_SCTP
if ((!fd_g_config->cnf_flags.no_sctp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_TCP)) {
struct next_conn * new;
CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
memset(new, 0, sizeof(struct next_conn));
fd_list_init(&new->chain, new);
new->proto = IPPROTO_SCTP;
new->port = ntohs(port_no); /* back to host byte order... */
new->dotls = dotls_immediate;
/* Add the new entry to the appropriate position (depending on preferences) */
if ((fd_g_config->cnf_flags.pr_tcp) || (peer->p_hdr.info.config.pic_flags.alg == PI_ALGPREF_TCP)) {
fd_list_insert_after(last_prio, &new->chain);
} else {
fd_list_insert_after(&peer->p_connparams, &new->chain); /* very first position */
}
count++;
}
#endif /* DISABLE_SCTP */
LOG_D("Prepared %d sets of connection parameters to peer %s", count, peer->p_hdr.info.pi_diamid);
return 0;
}
/* The thread that attempts the connection */
static void * connect_thr(void * arg)
{
struct fd_peer * peer = arg;
struct cnxctx * cnx = NULL;
struct next_conn * nc = NULL;
int rebuilt = 0;
int fatal_error=0;
TRACE_ENTRY("%p", arg);
CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL );
/* Set the thread name */
{
char buf[48];
snprintf(buf, sizeof(buf), "ConnTo:%s", peer->p_hdr.info.pi_diamid);
fd_log_threadname ( buf );
}
do {
/* Rebuild the list if needed, if it is empty -- but at most once */
if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
if (! rebuilt) {
CHECK_FCT_DO( fatal_error = prepare_connection_list(peer), goto out );
rebuilt ++;
}
if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
/* We encountered an error or we have looped over all the addresses of the peer. */
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "All connection attempts failed, will retry later", NULL);
CHECK_FCT_DO( fatal_error = fd_event_send(peer->p_events, FDEVP_CNX_FAILED, 0, NULL), goto out );
return NULL;
}
}
/* Attempt connection to the first entry */
nc = (struct next_conn *)(peer->p_connparams.next);
switch (nc->proto) {
case IPPROTO_TCP:
cnx = fd_cnx_cli_connect_tcp((sSA *)&nc->ss, sSAlen(&nc->ss));
break;
#ifndef DISABLE_SCTP
case IPPROTO_SCTP:
cnx = fd_cnx_cli_connect_sctp((peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ? 1 : fd_g_config->cnf_flags.no_ip6,
nc->port, &peer->p_hdr.info.pi_endpoints);
break;
#endif /* DISABLE_SCTP */
}
if (cnx)
break;
/* Pop these parameters and continue */
failed_connection_attempt(peer);
pthread_testcancel();
} while (!cnx); /* and until cancellation or all addresses attempted without success */
/* Now, we have an established connection in cnx */
pthread_cleanup_push((void *)fd_cnx_destroy, cnx);
/* Set the hostname in the connection, so that handshake verifies the remote identity */
fd_cnx_sethostname(cnx,peer->p_hdr.info.pi_diamid);
/* Handshake if needed (secure port) */
if (nc->dotls) {
CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT,
ALGO_HANDSHAKE_3436,
peer->p_hdr.info.config.pic_priority, NULL),
{
/* Handshake failed ... */
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS Handshake failed", NULL);
fd_cnx_destroy(cnx);
empty_connection_list(peer);
fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF);
goto out_pop;
} );
LOG_A("%s: TLS handshake successful.", peer->p_hdr.info.pi_diamid);
} else {
/* Prepare to receive the next message */
CHECK_FCT_DO( fatal_error = fd_cnx_start_clear(cnx, 0), goto out_pop );
}
/* Upon success, generate FDEVP_CNX_ESTABLISHED */
CHECK_FCT_DO( fatal_error = fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx), );
out_pop:
;
pthread_cleanup_pop(0);
out:
if (fatal_error) {
/* Cleanup the connection */
if (cnx)
fd_cnx_destroy(cnx);
/* Generate a termination event */
CHECK_FCT_DO(fd_core_shutdown(), );
}
return NULL;
}
/* Initiate a connection attempt to a remote peer */
int fd_p_cnx_init(struct fd_peer * peer)
{
TRACE_ENTRY("%p", peer);
/* Start the connect thread */
CHECK_FCT( pthread_create(&peer->p_ini_thr, NULL, connect_thr, peer) );
return 0;
}
/* Cancel a connection attempt */
void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all)
{
TRACE_ENTRY("%p %d", peer, cleanup_all);
CHECK_PARAMS_DO( CHECK_PEER(peer), return );
if (peer->p_ini_thr != (pthread_t)NULL) {
CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */);
failed_connection_attempt(peer);
}
if (cleanup_all) {
empty_connection_list(peer);
}
}
+207
View File
@@ -0,0 +1,207 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* This file contains code to handle Disconnect Peer messages (DPR and DPA) */
/* Delay to use before next reconnect attempt */
int fd_p_dp_newdelay(struct fd_peer * peer)
{
int delay = peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc;
switch (peer->p_hdr.info.runtime.pir_lastDC) {
case ACV_DC_REBOOTING:
default:
/* We use TcTimer to attempt reconnection */
break;
case ACV_DC_BUSY:
/* No need to hammer the overloaded peer */
delay *= 10;
break;
case ACV_DC_NOT_FRIEND:
/* He does not want to speak to us... let's retry a *lot* later maybe */
delay *= 200;
break;
}
return delay;
}
/* Handle a received message */
int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer)
{
long to_receive, to_send;
TRACE_ENTRY("%p %d %p", msg, req, peer);
if (req) {
/* We received a DPR, save the Disconnect-Cause and go to CLOSING_GRACE or terminate the connection */
struct avp * dc;
CHECK_FCT( fd_msg_search_avp ( *msg, fd_dict_avp_DC, &dc ));
if (dc) {
struct avp_hdr * hdr;
CHECK_FCT( fd_msg_avp_hdr( dc, &hdr ) );
if (hdr->avp_value == NULL) {
/* This is a sanity check */
LOG_F("BUG: Unset value in Disconnect-Cause in DPR");
ASSERT(0); /* To check if this really happens, and understand why... */
}
/* save the cause */
peer->p_hdr.info.runtime.pir_lastDC = hdr->avp_value->u32;
}
if (TRACE_BOOL(INFO)) {
if (dc) {
struct dict_object * dictobj;
struct dict_enumval_request er;
memset(&er, 0, sizeof(er));
/* prepare the request */
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT ) );
er.search.enum_value.u32 = peer->p_hdr.info.runtime.pir_lastDC;
/* Search the enum value */
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, 0 ) );
if (dictobj) {
CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with cause: %s", peer->p_hdr.info.pi_diamid, er.search.enum_name);
} else {
TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with unknown cause: %u", peer->p_hdr.info.pi_diamid, peer->p_hdr.info.runtime.pir_lastDC);
}
} else {
TRACE_DEBUG(INFO, "Peer '%s' sent a DPR without Disconnect-Cause AVP", peer->p_hdr.info.pi_diamid);
}
}
/* Now reply with a DPA */
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 ) );
/* Do we have pending exchanges with this peer? */
CHECK_FCT( fd_peer_get_load_pending(&peer->p_hdr, &to_receive, &to_send) );
if ((to_receive == 0) && (to_send == 0)) {
/* No pending exchange, move to CLOSING directly */
CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
/* Now send the DPA */
CHECK_FCT( fd_out_send( msg, NULL, peer, 0) );
/* and move to CLOSED */
fd_psm_cleanup(peer, 0);
/* Reset the timer for next connection attempt -- we'll retry sooner or later depending on the disconnection cause */
fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
} else {
/* We have pending exchanges, we move to CLOSING_GRACE which allows exchanges of answers but
not new requests */
CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) );
fd_psm_next_timeout(peer, 0, GRACE_TIMEOUT);
/* Now send the DPA */
CHECK_FCT( fd_out_send( msg, NULL, peer, 0) );
}
} else {
/* We received a DPA */
int curstate = fd_peer_getstate(peer);
if (curstate != STATE_CLOSING_GRACE) {
TRACE_DEBUG(INFO, "Ignoring DPA received in state %s", STATE_STR(curstate));
}
/* In theory, we should control the Result-Code AVP. But since we will not go back to OPEN state here anyway, let's skip it */
/* TODO("Control Result-Code in the DPA") */
CHECK_FCT_DO( fd_msg_free( *msg ), /* continue */ );
*msg = NULL;
/* Do we still have pending exchanges with this peer? */
CHECK_FCT( fd_peer_get_load_pending(&peer->p_hdr, &to_receive, &to_send) );
if ((to_receive != 0) || (to_send != 0)) {
TRACE_DEBUG(INFO, "Received DPA but pending load: [%ld, %ld], giving grace delay before closing", to_receive, to_send);
fd_psm_next_timeout(peer, 0, GRACE_TIMEOUT);
peer->p_flags.pf_localterm = 1;
} else {
/* otherwise, go to CLOSING state, the psm will handle terminating the connection */
CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
}
}
return 0;
}
/* Start disconnection of a peer: send DPR */
int fd_p_dp_initiate(struct fd_peer * peer, char * reason)
{
struct msg * msg = NULL;
struct dict_object * dictobj = NULL;
struct avp * avp = NULL;
struct dict_enumval_request er;
union avp_value val;
TRACE_ENTRY("%p %p", peer, reason);
/* Create a new DWR instance */
CHECK_FCT( fd_msg_new ( fd_dict_cmd_DPR, MSGFL_ALLOC_ETEID, &msg ) );
/* Add the Origin information */
CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
/* Add the Disconnect-Cause */
CHECK_FCT( fd_msg_avp_new ( fd_dict_avp_DC, 0, &avp ) );
/* Search the value in the dictionary */
memset(&er, 0, sizeof(er));
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT ) );
er.search.enum_name = reason ?: "REBOOTING";
CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT ), { ASSERT(0); /* internal error: unknown reason */ } );
CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
/* Set the value in the AVP */
val.u32 = er.search.enum_value.u32;
CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
/* Save the value also in the peer */
peer->p_hdr.info.runtime.pir_lastDC = val.u32;
/* Update the peer state and timer */
CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) );
fd_psm_next_timeout(peer, 0, DPR_TIMEOUT);
/* Now send the DPR message */
CHECK_FCT_DO( fd_out_send(&msg, NULL, peer, 0), /* ignore since we are on timeout anyway */ );
return 0;
}
+177
View File
@@ -0,0 +1,177 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* This file contains code to handle Device Watchdog messages (DWR and DWA) */
/* Check the value of Origin-State-Id is consistent in a DWR or DWA -- we return an error otherwise */
static int check_state_id(struct msg * msg, struct fd_peer * peer)
{
struct avp * osi;
/* Check if the request contains the Origin-State-Id */
CHECK_FCT( fd_msg_search_avp ( msg, fd_dict_avp_OSI, &osi ) );
if (osi) {
/* Check the value is consistent with the saved one */
struct avp_hdr * hdr;
CHECK_FCT( fd_msg_avp_hdr( osi, &hdr ) );
if (hdr->avp_value == NULL) {
/* This is a sanity check */
LOG_F("Ignored an Origin-State-Id AVP with unset value in DWR/DWA");
ASSERT(0); /* To check if this really happens, and understand why... */
}
if (! peer->p_hdr.info.runtime.pir_orstate) {
/* It was not already received in CER/CEA, save it now */
peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32;
}
if (peer->p_hdr.info.runtime.pir_orstate != hdr->avp_value->u32) {
TRACE_DEBUG(INFO, "Received a new Origin-State-Id from peer '%s'! (%x -> %x); resetting the connection.",
peer->p_hdr.info.pi_diamid,
peer->p_hdr.info.runtime.pir_orstate,
hdr->avp_value->u32 );
return EINVAL;
}
}
return 0;
}
/* Create and send a DWR */
static int send_DWR(struct fd_peer * peer)
{
struct msg * msg = NULL;
/* Create a new DWR instance */
CHECK_FCT( fd_msg_new ( fd_dict_cmd_DWR, MSGFL_ALLOC_ETEID, &msg ) );
/* Add the content of the message (only the origin) */
CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
/* Now send this message */
CHECK_FCT( fd_out_send(&msg, NULL, peer, 0) );
/* And mark the pending DW */
peer->p_flags.pf_dw_pending = 1;
return 0;
}
/* Handle an incoming message */
int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer)
{
int reset_tmr = 0;
TRACE_ENTRY("%p %d %p", msg, req, peer);
/* Check the value of OSI for information */
CHECK_FCT( check_state_id(*msg, peer) );
if (req) {
/* If we receive a DWR, send back a DWA */
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, 0 ) );
CHECK_FCT( fd_msg_add_origin ( *msg, 1 ) );
CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer, 0) );
} else {
/* Discard the DWA */
CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
*msg = NULL;
/* And clear the pending DW flag */
peer->p_flags.pf_dw_pending = 0;
}
/* Now update timeout */
if (req) {
/* Update timeout only if we did not already send a DWR ourselves */
reset_tmr = !peer->p_flags.pf_dw_pending;
} else {
/* Reset the timer */
reset_tmr = 1;
}
if (reset_tmr) {
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
}
/* If we are in REOPEN state, increment the counter */
if (fd_peer_getstate(peer) == STATE_REOPEN) {
peer->p_flags.pf_reopen_cnt += 1;
if (peer->p_flags.pf_reopen_cnt) {
/* Send a new DWR */
CHECK_FCT( send_DWR(peer) );
} else {
/* Move to OPEN state */
CHECK_FCT( fd_psm_change_state(peer, STATE_OPEN) );
}
}
return 0;
}
/* Handle a timeout in the PSM (OPEN or REOPEN state only) */
int fd_p_dw_timeout(struct fd_peer * peer)
{
TRACE_ENTRY("%p", peer);
if (peer->p_flags.pf_dw_pending) {
/* We have sent a DWR and received no answer during TwTimer */
CHECK_FCT( fd_psm_change_state(peer, STATE_SUSPECT) );
fd_psm_next_timeout(peer, 0, 2 * (peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw) );
} else {
/* The timeout has expired, send a DWR */
CHECK_FCT( send_DWR(peer) );
fd_psm_next_timeout(peer, 0, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw );
}
return 0;
}
/* Handle DW exchanges after the peer has come alive again */
int fd_p_dw_reopen(struct fd_peer * peer)
{
TRACE_ENTRY("%p", peer);
peer->p_flags.pf_reopen_cnt = 1;
peer->p_flags.pf_cnx_pb = 0;
CHECK_FCT( send_DWR(peer) );
return 0;
}
+205
View File
@@ -0,0 +1,205 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* Delay for garbage collection of expired peers, in seconds */
#define GC_TIME 120
static pthread_t exp_thr = (pthread_t)NULL;
static pthread_t gc_thr = (pthread_t)NULL;
static struct fd_list exp_list = FD_LIST_INITIALIZER( exp_list );
static pthread_cond_t exp_cnd = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t exp_mtx = PTHREAD_MUTEX_INITIALIZER;
static void * gc_th_fct(void * arg)
{
fd_log_threadname ( "Peers/garb. col." );
TRACE_ENTRY( "%p", arg );
do {
struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
sleep(GC_TIME); /* sleep is a cancellation point */
/* Now check in the peers list if any peer can be deleted */
CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), goto error );
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * peer = (struct fd_peer *)li->o;
if (fd_peer_getstate(peer) != STATE_ZOMBIE)
continue;
if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_ALWAYS)
continue; /* This peer was not supposed to terminate, keep it in the list for debug */
/* Ok, the peer was expired, let's remove it */
li = li->prev; /* to avoid breaking the loop */
fd_list_unlink(&peer->p_hdr.chain);
fd_list_insert_before(&purge, &peer->p_hdr.chain);
}
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), goto error );
/* Now delete peers that are in the purge list */
while (!FD_IS_LIST_EMPTY(&purge)) {
struct fd_peer * peer = (struct fd_peer *)(purge.next->o);
fd_list_unlink(&peer->p_hdr.chain);
TRACE_DEBUG(INFO, "Garbage Collect: delete zombie peer '%s'", peer->p_hdr.info.pi_diamid);
CHECK_FCT_DO( fd_peer_free(&peer), /* Continue... what else to do ? */ );
}
} while (1);
error:
TRACE_DEBUG(INFO, "An error occurred in peers module! GC thread is terminating...");
ASSERT(0);
CHECK_FCT_DO(fd_core_shutdown(), );
return NULL;
}
static void * exp_th_fct(void * arg)
{
fd_log_threadname ( "Peers/expire" );
TRACE_ENTRY( "%p", arg );
CHECK_POSIX_DO( pthread_mutex_lock(&exp_mtx), { ASSERT(0); } );
pthread_cleanup_push( fd_cleanup_mutex, &exp_mtx );
do {
struct timespec now;
struct fd_peer * first;
/* Check if there are expiring peers available */
if (FD_IS_LIST_EMPTY(&exp_list)) {
/* Just wait for a change or cancelation */
CHECK_POSIX_DO( pthread_cond_wait( &exp_cnd, &exp_mtx ), { ASSERT(0); } );
/* Restart the loop on wakeup */
continue;
}
/* Get the pointer to the peer that expires first */
first = (struct fd_peer *)(exp_list.next->o);
ASSERT( CHECK_PEER(first) );
/* Get the current time */
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), { ASSERT(0); } );
/* If first peer is not expired, we just wait until it happens */
if ( TS_IS_INFERIOR( &now, &first->p_exp_timer ) ) {
CHECK_POSIX_DO2( pthread_cond_timedwait( &exp_cnd, &exp_mtx, &first->p_exp_timer ),
ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
/* on other error, */ { ASSERT(0); } );
/* on wakeup, loop */
continue;
}
/* Now, the first peer in the list is expired; signal it */
fd_list_unlink( &first->p_expiry );
CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, 0, "DO_NOT_WANT_TO_TALK_TO_YOU"), break );
} while (1);
pthread_cleanup_pop( 1 );
TRACE_DEBUG(INFO, "An error occurred in peers module! Expiry thread is terminating...");
CHECK_FCT_DO(fd_core_shutdown(), );
return NULL;
}
/* Initialize peers expiry mechanism */
int fd_p_expi_init(void)
{
TRACE_ENTRY();
CHECK_FCT( pthread_create( &exp_thr, NULL, exp_th_fct, NULL ) );
CHECK_FCT( pthread_create( &gc_thr, NULL, gc_th_fct, NULL ) );
return 0;
}
/* Finish peers expiry mechanism */
int fd_p_expi_fini(void)
{
CHECK_FCT_DO( fd_thr_term(&exp_thr), );
CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
while (!FD_IS_LIST_EMPTY(&exp_list)) {
struct fd_peer * peer = (struct fd_peer *)(exp_list.next->o);
fd_list_unlink(&peer->p_expiry );
}
CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
CHECK_FCT_DO( fd_thr_term(&gc_thr), );
return 0;
}
/* Add / requeue a peer in the expiry list */
int fd_p_expi_update(struct fd_peer * peer )
{
TRACE_ENTRY("%p", peer);
CHECK_PARAMS( CHECK_PEER(peer) );
CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
fd_list_unlink(&peer->p_expiry );
/* if peer expires */
if (peer->p_hdr.info.config.pic_flags.exp) {
struct fd_list * li;
/* update the p_exp_timer value */
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer), { ASSERT(0); } );
peer->p_exp_timer.tv_sec += peer->p_hdr.info.config.pic_lft;
/* add to the expiry list in appropriate position (probably around the end) */
for (li = exp_list.prev; li != &exp_list; li = li->prev) {
struct fd_peer * p = (struct fd_peer *)(li->o);
if (TS_IS_INFERIOR( &p->p_exp_timer, &peer->p_exp_timer ) )
break;
}
fd_list_insert_after(li, &peer->p_expiry);
/* signal the expiry thread if we added in first position */
if (li == &exp_list) {
CHECK_POSIX( pthread_cond_signal(&exp_cnd) );
}
}
CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
return 0;
}
+237
View File
@@ -0,0 +1,237 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* Alloc a new hbh for requests, bufferize the message and send on the connection, save in sentreq if provided */
static int do_send(struct msg ** msg, struct cnxctx * cnx, uint32_t * hbh, struct fd_peer * peer)
{
struct msg_hdr * hdr;
int msg_is_a_req;
uint8_t * buf;
size_t sz;
int ret;
uint32_t bkp_hbh = 0;
struct msg *cpy_for_logs_only;
TRACE_ENTRY("%p %p %p %p", msg, cnx, hbh, peer);
/* Retrieve the message header */
CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
msg_is_a_req = (hdr->msg_flags & CMD_FLAG_REQUEST);
if (msg_is_a_req) {
CHECK_PARAMS(hbh && peer);
/* Alloc the hop-by-hop id and increment the value for next message */
bkp_hbh = hdr->msg_hbhid;
hdr->msg_hbhid = *hbh;
*hbh = hdr->msg_hbhid + 1;
}
/* Create the message buffer */
CHECK_FCT(fd_msg_bufferize( *msg, &buf, &sz ));
pthread_cleanup_push( free, buf );
cpy_for_logs_only = *msg;
/* Save a request before sending so that there is no race condition with the answer */
if (msg_is_a_req) {
CHECK_FCT_DO( ret = fd_p_sr_store(&peer->p_sr, msg, &hdr->msg_hbhid, bkp_hbh), goto out );
}
/* Log the message */
fd_hook_call(HOOK_MESSAGE_SENT, cpy_for_logs_only, peer, NULL, fd_msg_pmdl_get(cpy_for_logs_only));
pthread_cleanup_push((void *)fd_msg_free, *msg /* might be NULL, no problem */);
/* Send the message */
CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz), );
pthread_cleanup_pop(0);
out:
;
pthread_cleanup_pop(1);
if (ret)
return ret;
/* Free remaining messages (i.e. answers) */
if (*msg) {
CHECK_FCT( fd_msg_free(*msg) );
*msg = NULL;
}
return 0;
}
/* The code of the "out" thread */
static void * out_thr(void * arg)
{
struct fd_peer * peer = arg;
int stop = 0;
struct msg * msg;
ASSERT( CHECK_PEER(peer) );
/* Set the thread name */
{
char buf[48];
snprintf(buf, sizeof(buf), "OUT/%s", peer->p_hdr.info.pi_diamid);
fd_log_threadname ( buf );
}
/* Loop until cancelation */
while (!stop) {
int ret;
/* Retrieve next message to send */
CHECK_FCT_DO( fd_fifo_get(peer->p_tosend, &msg), goto error );
/* Send the message, log any error */
CHECK_FCT_DO( ret = do_send(&msg, peer->p_cnxctx, &peer->p_hbh, peer),
{
if (msg) {
char buf[256];
snprintf(buf, sizeof(buf), "Error while sending this message: %s", strerror(ret));
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
fd_msg_free(msg);
}
stop = 1;
} );
}
/* If we're here it means there was an error on the socket. We need to continue to purge the fifo & until we are canceled */
CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ );
/* Requeue all routable messages in the global "out" queue, until we are canceled once the PSM deals with the CNX_ERROR sent above */
while ( fd_fifo_get(peer->p_tosend, &msg) == 0 ) {
if (fd_msg_is_routable(msg)) {
CHECK_FCT_DO(fd_fifo_post_noblock(peer->p_tofailover, (void *)&msg),
{
/* fallback: destroy the message */
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, "Internal error: unable to requeue this message during failover process", fd_msg_pmdl_get(msg));
CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */)
} );
} else {
/* Just free it */
/* fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Non-routable message freed during handover", fd_msg_pmdl_get(m)); */
CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */)
}
}
error:
/* It is not really a connection error, but the effect is the same, we are not able to send anymore message */
CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ );
return NULL;
}
/* Wrapper to sending a message either by out thread (peer in OPEN state) or directly; cnx or peer must be provided. Flags are valid only for direct sending, not through thread (unused) */
int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, int update_reqin_cnt)
{
struct msg_hdr * hdr;
TRACE_ENTRY("%p %p %p", msg, cnx, peer);
CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx)));
fd_hook_call(HOOK_MESSAGE_SENDING, *msg, peer, NULL, fd_msg_pmdl_get(*msg));
if (update_reqin_cnt && peer) {
CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
/* Update the count of pending answers to send */
CHECK_POSIX( pthread_mutex_lock(&peer->p_state_mtx) );
peer->p_reqin_count--;
CHECK_POSIX( pthread_mutex_unlock(&peer->p_state_mtx) );
}
}
if (fd_peer_getstate(peer) == STATE_OPEN) {
/* Normal case: just queue for the out thread to pick it up */
CHECK_FCT( fd_fifo_post(peer->p_tosend, msg) );
} else {
int ret;
uint32_t *hbh = NULL;
/* In other cases, the thread is not running, so we handle the sending directly */
if (peer)
hbh = &peer->p_hbh;
if (!cnx)
cnx = peer->p_cnxctx;
/* Do send the message */
CHECK_FCT_DO( ret = do_send(msg, cnx, hbh, peer),
{
if (msg) {
char buf[256];
snprintf(buf, sizeof(buf), "Error while sending this message: %s", strerror(ret));
fd_hook_call(HOOK_MESSAGE_DROPPED, *msg, NULL, buf, fd_msg_pmdl_get(*msg));
fd_msg_free(*msg);
*msg = NULL;
}
} );
}
return 0;
}
/* Start the "out" thread that picks messages in p_tosend and send them on p_cnxctx */
int fd_out_start(struct fd_peer * peer)
{
TRACE_ENTRY("%p", peer);
CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_outthr == (pthread_t)NULL) );
CHECK_POSIX( pthread_create(&peer->p_outthr, NULL, out_thr, peer) );
CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 1) );
return 0;
}
/* Stop that thread */
int fd_out_stop(struct fd_peer * peer)
{
TRACE_ENTRY("%p", peer);
CHECK_PARAMS( CHECK_PEER(peer) );
CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 0) );
CHECK_FCT( fd_thr_term(&peer->p_outthr) );
return 0;
}
+958
View File
@@ -0,0 +1,958 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/*
This file implement a Peer State Machine which is a mix of:
- the state machine described in rfc3588bis
- the state machine described in rfc3539#section-3.4
- the following observations.
The delivery of Diameter messages must not always be unordered: order is important at
begining and end of a connection lifetime. It means we need agility to
switch between "ordering enforced" and "ordering not enforced to counter
Head of the Line Blocking" modes of operation.
The connection state machine represented in RFC3588 (and RFC6733) is
incomplete, because it lacks the SUSPECT state and the 3 DWR/DWA
exchanges (section 5.1) when the peer recovers from this state.
Personnally I don't see the rationale for exchanging 3 messages (why 3?)
but, if we require at least 1 DWR/DWA exchange to be always performed
after the CER/CEA exchange (and initiated by the peer that sent the
CEA), we have a simple way to deal with our ordering problem, as resumed
below. Peers are: [i]nitiator, [r]esponder.
(1) [i] SCTP connection attempt.
(2) [r] accept the connection.
(3) [i,r] (if secure port) DTLS handshake, close on failure.
(4) [i] Send CER
(5) [r] Receive CER, send CEA using stream 0, flag "unordered" cleared.
[r] Immediately send a DWR after the CEA, also using stream 0,
flag "unordered" cleared.
[r] Move to STATE_OPEN_NEW state -- equivalent to OPEN except
that all messages are sent ordered at the moment.
(6) [i] receive CEA, move to OPEN state. All messages can be sent
unordered in OPEN state.
[i] As per normal operation, reply with DWA to the DWR.
(7) [r] Upon reception of the DWA, move to OPEN state, messages can be
sent unordered from this point.
Note about (5) and (6): if the Diameter Identity received in CER or CEA
does not match the credentials from the certificate presented during
TLS handshake, we may need to specify a path of clean disconnection
(not blocking the remote peer waiting for something).
This proposed mechanism removes the problem of application messages
received before the CEA by the initiator. Note that if the "old" inband
TLS handshake is used, this handshake plays the same synchronization
role than the new DWR/DWA, which becomes useless.
The other time where ordering is important is by the end of connection
lifetime, when one peer is shutting down the link for some reason
(reboot, overload, no activity, etc...). In case of unordered delivery,
we may have:
- peer A sends an application message followed by a DPR. Peer B receives
the DPR first and tears down the connection. Application message is lost.
- Peer B sends an application message, then receives a DPR and answers a
DPA. Peer A receives the DPA before the application message. The
application message is lost.
This situation is actually happening easily because DPR/DPA messages are
very short, while application messages can be quite large. Therefore,
they require much more time to deliver.
I really cannot see a way to counter this effect by using the ordering
of the messages, except by applying a timer (state STATE_CLOSING_GRACE).
This timer can be also useful when we detect that some messages has not
yet received an answer on this link, to give time to the application to
complete the exchange ongoing.
However, this problem must be balanced with the fact that the message
that is lost will be in many cases sent again as the failover mechanism
specifies.
*/
/* The actual declaration of peer_state_str */
DECLARE_STATE_STR();
/* Helper for next macro */
#define case_str( _val ) \
case _val : return #_val
DECLARE_PEV_STR();
/************************************************************************/
/* Delayed startup */
/************************************************************************/
static int started = 0;
static pthread_mutex_t started_mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t started_cnd = PTHREAD_COND_INITIALIZER;
/* Wait for start signal */
static int fd_psm_waitstart()
{
int ret = 0;
TRACE_ENTRY("");
CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
awake:
if (!ret && !started) {
pthread_cleanup_push( fd_cleanup_mutex, &started_mtx );
CHECK_POSIX_DO( ret = pthread_cond_wait(&started_cnd, &started_mtx), );
pthread_cleanup_pop( 0 );
goto awake;
}
CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
return ret;
}
/* Allow the state machines to start */
int fd_psm_start()
{
TRACE_ENTRY("");
CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
started = 1;
CHECK_POSIX( pthread_cond_broadcast(&started_cnd) );
CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
return 0;
}
/************************************************************************/
/* Manage the list of active peers */
/************************************************************************/
/* Enter/leave OPEN state */
static int enter_open_state(struct fd_peer * peer)
{
struct fd_list * li;
CHECK_PARAMS( FD_IS_LIST_EMPTY(&peer->p_actives) );
/* Callback registered by the credential validator (fd_peer_validate_register) */
if (peer->p_cb2) {
CHECK_FCT_DO( (*peer->p_cb2)(&peer->p_hdr.info),
{
TRACE_DEBUG(FULL, "Validation failed, terminating the connection");
fd_psm_terminate(peer, "DO_NOT_WANT_TO_TALK_TO_YOU" );
} );
peer->p_cb2 = NULL;
return 0;
}
/* Insert in the active peers list */
CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
struct fd_peer * next_p = (struct fd_peer *)li->o;
int cmp = fd_os_cmp(peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen,
next_p->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamidlen);
if (cmp < 0)
break;
}
fd_list_insert_before(li, &peer->p_actives);
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
/* Callback registered when the peer was added, by fd_peer_add */
if (peer->p_cb) {
LOG_N("Calling add callback for peer %s", peer->p_hdr.info.pi_diamid);
(*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data); /* TODO: do this in a separate detached thread? */
// peer->p_cb = NULL;
// peer->p_cb_data = NULL;
}
/* Start the thread to handle outgoing messages */
CHECK_FCT( fd_out_start(peer) );
/* Update the expiry timer now */
CHECK_FCT( fd_p_expi_update(peer) );
return 0;
}
static int leave_open_state(struct fd_peer * peer, int skip_failover)
{
/* Remove from active peers list */
CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
fd_list_unlink( &peer->p_actives );
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
/* Stop the "out" thread */
CHECK_FCT( fd_out_stop(peer) );
/* Failover the messages */
if (!skip_failover) {
fd_peer_failover_msg(peer);
}
return 0;
}
/************************************************************************/
/* Helpers for state changes */
/************************************************************************/
/* Cleanup pending events in the peer */
void fd_psm_events_free(struct fd_peer * peer)
{
struct fd_event * ev;
/* Purge all events, and free the associated data if any */
while (fd_fifo_tryget( peer->p_events, &ev ) == 0) {
switch (ev->code) {
case FDEVP_CNX_ESTABLISHED: {
fd_cnx_destroy(ev->data);
}
break;
case FDEVP_TERMINATE:
/* Do not free the string since it is a constant */
break;
case FDEVP_CNX_INCOMING: {
struct cnx_incoming * evd = ev->data;
fd_hook_call(HOOK_MESSAGE_DROPPED, evd->cer, NULL, "Message discarded while cleaning peer state machine queue.", fd_msg_pmdl_get(evd->cer));
CHECK_FCT_DO( fd_msg_free(evd->cer), /* continue */);
fd_cnx_destroy(evd->cnx);
}
default:
free(ev->data);
}
free(ev);
}
}
/* Read state */
int fd_peer_get_state(struct peer_hdr *peer)
{
int ret;
struct fd_peer * p = (struct fd_peer *)peer;
if (!CHECK_PEER(p))
return -1;
CHECK_POSIX_DO( pthread_mutex_lock(&p->p_state_mtx), return -1 );
ret = p->p_state;
CHECK_POSIX_DO( pthread_mutex_unlock(&p->p_state_mtx), return -1 );
return ret;
}
/* Change state */
int fd_psm_change_state(struct fd_peer * peer, int new_state)
{
int old;
TRACE_ENTRY("%p %d(%s)", peer, new_state, STATE_STR(new_state));
CHECK_PARAMS( CHECK_PEER(peer) );
old = fd_peer_getstate(peer);
if (old == new_state)
return 0;
LOG(((old == STATE_OPEN) || (new_state == STATE_OPEN)) ? FD_LOG_NOTICE : FD_LOG_DEBUG, "'%s'\t-> '%s'\t'%s'",
STATE_STR(old),
STATE_STR(new_state),
peer->p_hdr.info.pi_diamid);
CHECK_POSIX( pthread_mutex_lock(&peer->p_state_mtx) );
peer->p_state = new_state;
CHECK_POSIX( pthread_mutex_unlock(&peer->p_state_mtx) );
if (old == STATE_OPEN) {
CHECK_FCT( leave_open_state(peer, new_state == STATE_CLOSING_GRACE) );
}
if (old == STATE_CLOSING_GRACE) {
fd_peer_failover_msg(peer);
}
if (new_state == STATE_OPEN) {
CHECK_FCT( enter_open_state(peer) );
}
if (new_state == STATE_CLOSED) {
/* Purge event list */
fd_psm_events_free(peer);
/* Reset the counter of pending anwers to send */
peer->p_reqin_count = 0;
/* If the peer is not persistant, we destroy it */
if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_NONE) {
CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
}
}
return 0;
}
/* Set timeout timer of next event */
void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay)
{
TRACE_DEBUG(FULL, "Peer timeout reset to %d seconds%s", delay, add_random ? " (+/- 2)" : "" );
/* Initialize the timer */
CHECK_POSIX_DO( clock_gettime( CLOCK_REALTIME, &peer->p_psm_timer ), ASSERT(0) );
if (add_random) {
if (delay > 2)
delay -= 2;
else
delay = 0;
/* Add a random value between 0 and 4sec */
peer->p_psm_timer.tv_sec += random() % 4;
peer->p_psm_timer.tv_nsec+= random() % 1000000000L;
if (peer->p_psm_timer.tv_nsec >= 1000000000L) {
peer->p_psm_timer.tv_nsec -= 1000000000L;
peer->p_psm_timer.tv_sec ++;
}
}
peer->p_psm_timer.tv_sec += delay;
#ifdef SLOW_PSM
/* temporary for debug */
peer->p_psm_timer.tv_sec += 10;
#endif
}
/* Cleanup the peer */
void fd_psm_cleanup(struct fd_peer * peer, int terminate)
{
/* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */
if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ );
}
fd_p_cnx_abort(peer, terminate);
fd_p_ce_clear_cnx(peer, NULL);
if (peer->p_receiver) {
fd_cnx_destroy(peer->p_receiver);
peer->p_receiver = NULL;
}
if (terminate) {
fd_psm_events_free(peer);
CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
}
}
/************************************************************************/
/* The PSM thread */
/************************************************************************/
/* Cancelation cleanup : set ZOMBIE state in the peer */
void cleanup_setstate(void * arg)
{
struct fd_peer * peer = (struct fd_peer *)arg;
CHECK_PARAMS_DO( CHECK_PEER(peer), return );
CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
peer->p_state = STATE_ZOMBIE;
CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
return;
}
/* The state machine thread (controler) */
static void * p_psm_th( void * arg )
{
struct fd_peer * peer = (struct fd_peer *)arg;
int created_started = started ? 1 : 0;
int event;
size_t ev_sz;
void * ev_data;
int cur_state;
CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) );
pthread_cleanup_push( cleanup_setstate, arg );
/* Set the thread name */
{
char buf[48];
snprintf(buf, sizeof(buf), "PSM/%s", peer->p_hdr.info.pi_diamid);
fd_log_threadname ( buf );
}
/* The state machine starts in CLOSED state */
CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), goto psm_end );
peer->p_state = STATE_CLOSED;
CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), goto psm_end );
/* Wait that the PSM are authorized to start in the daemon */
CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end );
/* Initialize the timer */
if (peer->p_flags.pf_responder) {
fd_psm_next_timeout(peer, 0, INCNX_TIMEOUT);
} else {
fd_psm_next_timeout(peer, created_started, 0);
}
psm_loop:
/* Get next event */
TRACE_DEBUG(FULL, "'%s' in state '%s' waiting for next event.",
peer->p_hdr.info.pi_diamid, STATE_STR(fd_peer_getstate(peer)));
CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end );
cur_state = fd_peer_getstate(peer);
if (cur_state == -1)
goto psm_end;
TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'",
STATE_STR(cur_state),
fd_pev_str(event), ev_data, ev_sz,
peer->p_hdr.info.pi_diamid);
/* Now, the action depends on the current state and the incoming event */
/* The following states are impossible */
ASSERT( cur_state != STATE_NEW );
ASSERT( cur_state != STATE_ZOMBIE );
ASSERT( cur_state != STATE_OPEN_HANDSHAKE ); /* because it should exist only between two loops */
/* Purge invalid events */
if (!CHECK_PEVENT(event)) {
TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event);
ASSERT(0); /* we should investigate this situation */
goto psm_loop;
}
/* Requests to terminate the peer object */
if (event == FDEVP_TERMINATE) {
switch (cur_state) {
case STATE_OPEN:
case STATE_OPEN_NEW:
case STATE_REOPEN:
/* We cannot just close the connection, we have to send a DPR first */
CHECK_FCT_DO( fd_p_dp_initiate(peer, ev_data), goto psm_end );
goto psm_loop;
/*
case STATE_CLOSING:
case STATE_CLOSING_GRACE:
case STATE_WAITCNXACK:
case STATE_WAITCNXACK_ELEC:
case STATE_WAITCEA:
case STATE_SUSPECT:
case STATE_CLOSED:
*/
default:
/* In these cases, we just cleanup the peer object (if needed) and terminate */
goto psm_end;
}
}
/* A message was received */
if (event == FDEVP_CNX_MSG_RECV) {
struct msg * msg = NULL;
struct msg_hdr * hdr;
struct fd_cnx_rcvdata rcv_data;
struct fd_msg_pmdl * pmdl = NULL;
rcv_data.buffer = ev_data;
rcv_data.length = ev_sz;
pmdl = fd_msg_pmdl_get_inbuf(rcv_data.buffer, rcv_data.length);
/* Parse the received buffer */
CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg),
{
fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, NULL, peer, &rcv_data, pmdl );
free(ev_data);
CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
goto psm_loop;
} );
fd_hook_associate(msg, pmdl);
CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen), goto psm_end);
/* If the current state does not allow receiving messages, just drop it */
if (cur_state == STATE_CLOSED) {
/* In such case, just discard the message */
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, "Message purged from queue, peer in CLOSED state", fd_msg_pmdl_get(msg));
fd_msg_free(msg);
goto psm_loop;
}
/* Extract the header */
CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end );
/* If it is an answer, associate with the request or drop */
if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
struct msg * req;
/* Search matching request (same hbhid) */
CHECK_FCT_DO( fd_p_sr_fetch(&peer->p_sr, hdr->msg_hbhid, &req), goto psm_end );
if (req == NULL) {
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, "Answer received with no corresponding sent request.", fd_msg_pmdl_get(msg));
fd_msg_free(msg);
goto psm_loop;
}
/* Associate */
CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end );
}
/* Log incoming message */
fd_hook_call(HOOK_MESSAGE_RECEIVED, msg, peer, NULL, fd_msg_pmdl_get(msg));
if (cur_state == STATE_OPEN_NEW) {
/* OK, we have received something, so the connection is supposedly now in OPEN state at the remote site */
fd_psm_change_state(peer, STATE_OPEN );
}
/* Now handle non-link-local messages */
if (fd_msg_is_routable(msg)) {
switch (cur_state) {
/* To maximize compatibility -- should not be a security issue here */
case STATE_REOPEN:
case STATE_SUSPECT:
case STATE_CLOSING:
case STATE_CLOSING_GRACE:
TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state... ");
/* The standard situation : */
case STATE_OPEN_NEW:
case STATE_OPEN:
/* We received a valid routable message, update the expiry timer */
CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end );
/* Set the message source and add the Route-Record */
CHECK_FCT_DO( fd_msg_source_setrr( msg, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, fd_g_config->cnf_dict ), goto psm_end);
if ((hdr->msg_flags & CMD_FLAG_REQUEST)) {
/* Mark the incoming request so that we know we have pending answers for this peer */
CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), goto psm_end );
peer->p_reqin_count++;
CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), goto psm_end );
}
/* Requeue to the global incoming queue */
CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end );
/* Update the peer timer (only in OPEN state) */
if ((cur_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) {
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
}
break;
/* In other states, we discard the message, it is either old or invalid to send it for the remote peer */
case STATE_WAITCNXACK:
case STATE_WAITCNXACK_ELEC:
case STATE_WAITCEA:
case STATE_CLOSED:
default: {
/* In such case, just discard the message */
char buf[128];
snprintf(buf, sizeof(buf), "Received while peer state machine was in state %s.", STATE_STR(cur_state));
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
fd_msg_free(msg);
}
}
goto psm_loop;
}
/* Link-local message: They must be understood by our dictionary, otherwise we return an error */
{
struct msg * error = NULL;
int ret = fd_msg_parse_or_error( &msg, &error );
if (ret != EBADMSG) {
CHECK_FCT_DO( ret,
{
char buf[256];
snprintf(buf, sizeof(buf), "%s: An unexpected error occurred while parsing a link-local message", peer->p_hdr.info.pi_diamid);
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
fd_msg_free(msg);
goto psm_end;
} );
} else {
if (msg == NULL) {
/* Send the error back to the peer */
CHECK_FCT_DO( ret = fd_out_send(&error, NULL, peer, 0), );
if (error) {
char buf[256];
/* Only if an error occurred & the message was not saved / dumped */
snprintf(buf, sizeof(buf), "%s: error sending a message", peer->p_hdr.info.pi_diamid);
fd_hook_call(HOOK_MESSAGE_DROPPED, error, peer, buf, fd_msg_pmdl_get(error));
CHECK_FCT_DO( fd_msg_free(error), goto psm_end);
}
} else {
char buf[256];
/* We received an invalid answer, let's disconnect */
snprintf(buf, sizeof(buf), "%s: Received invalid answer to Base protocol message, disconnecting...", peer->p_hdr.info.pi_diamid);
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
CHECK_FCT_DO( fd_msg_free(msg), goto psm_end);
CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
}
goto psm_loop;
}
}
/* Handle the LL message and update the expiry timer appropriately */
switch (hdr->msg_code) {
case CC_CAPABILITIES_EXCHANGE:
CHECK_FCT_DO( fd_p_ce_msgrcv(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer),
{
if (msg)
CHECK_FCT_DO( fd_msg_free(msg), );
goto psm_reset;
} );
break;
case CC_DISCONNECT_PEER:
CHECK_FCT_DO( fd_p_dp_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
if (fd_peer_getstate(peer) == STATE_CLOSING)
goto psm_end;
break;
case CC_DEVICE_WATCHDOG:
/* We received a WDA message, update the expiry timer */
if (!(hdr->msg_flags & CMD_FLAG_REQUEST))
{
fd_p_expi_update(peer);
}
CHECK_FCT_DO( fd_p_dw_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
break;
default:
/* Unknown / unexpected / invalid message -- but validated by our dictionary */
TRACE_DEBUG(INFO, "Invalid non-routable command received: %u.", hdr->msg_code);
if (hdr->msg_flags & CMD_FLAG_REQUEST) {
do {
/* Reply with an error code */
CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ), break );
/* Set the error code */
CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_COMMAND_UNSUPPORTED", "Or maybe the P-bit or application Id are erroneous.", NULL, 1 ), break );
/* Send the answer */
CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, 0), break );
} while (0);
} else {
/* We did ASK for it ??? */
TRACE_DEBUG(INFO, "Received answer with erroneous 'is_routable' result...");
}
/* Cleanup the message if not done */
if (msg) {
char buf[256];
snprintf(buf, sizeof(buf), "Received un-handled non-routable command from peer '%s'.", peer->p_hdr.info.pi_diamid);
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
msg = NULL;
}
};
/* At this point the message must have been fully handled already */
if (msg) {
char buf[256];
snprintf(buf, sizeof(buf), "Internal error ('%s'): unhandled message.", peer->p_hdr.info.pi_diamid);
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
fd_msg_free(msg);
}
goto psm_loop;
}
/* The connection object is broken */
if (event == FDEVP_CNX_ERROR) {
switch (cur_state) {
case STATE_WAITCNXACK_ELEC:
/* Abort the initiating side */
fd_p_cnx_abort(peer, 0);
/* Process the receiver side */
CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
break;
case STATE_WAITCEA:
case STATE_OPEN:
case STATE_OPEN_NEW:
case STATE_REOPEN:
case STATE_WAITCNXACK:
case STATE_SUSPECT:
default:
/* Mark the connection problem */
peer->p_flags.pf_cnx_pb = 1;
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "The connection was broken", NULL);
/* Destroy the connection, restart the timer to a new connection attempt */
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
case STATE_CLOSED:
goto psm_reset;
case STATE_CLOSING:
/* We sent a DPR so we are terminating, do not wait for DPA */
goto psm_end;
case STATE_CLOSING_GRACE:
if (peer->p_flags.pf_localterm) /* initiated here */
goto psm_end;
fd_psm_cleanup(peer, 0);
/* Reset the timer for next connection attempt */
fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
goto psm_loop;
}
goto psm_loop;
}
/* The connection notified a change in endpoints */
if (event == FDEVP_CNX_EP_CHANGE) {
/* We actually don't care if we are in OPEN state here... */
/* Cleanup the remote LL and primary addresses */
CHECK_FCT_DO( fd_ep_filter( &peer->p_hdr.info.pi_endpoints, EP_FL_CONF | EP_FL_DISC | EP_FL_ADV ), /* ignore the error */);
CHECK_FCT_DO( fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_PRIMARY ), /* ignore the error */);
/* Get the new ones */
CHECK_FCT_DO( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints), /* ignore the error */);
/* We do not support local endpoints change currently, but it could be added here if needed (refresh fd_g_config->cnf_endpoints) */
{
char * buf = NULL;
size_t len = 0;
LOG_D("Got low layer notification (IGNORED): remote endpoint(s) changed: %s", fd_ep_dump(&buf, &len, NULL, 0, 0, &peer->p_hdr.info.pi_endpoints) ?: "error");
free(buf);
}
/* Done */
goto psm_loop;
}
/* A new connection was established and CER containing this peer id was received */
if (event == FDEVP_CNX_INCOMING) {
struct cnx_incoming * params = ev_data;
ASSERT(params);
/* Handle the message */
CHECK_FCT_DO( fd_p_ce_handle_newCER(&params->cer, peer, &params->cnx, params->validate), goto psm_end );
/* Cleanup if needed */
if (params->cnx) {
fd_cnx_destroy(params->cnx);
params->cnx = NULL;
}
if (params->cer) {
CHECK_FCT_DO( fd_msg_free(params->cer), );
params->cer = NULL;
}
/* Loop */
free(ev_data);
goto psm_loop;
}
/* A new connection has been established with the remote peer */
if (event == FDEVP_CNX_ESTABLISHED) {
struct cnxctx * cnx = ev_data;
/* 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;
switch (cur_state) {
case STATE_WAITCNXACK_ELEC:
case STATE_WAITCNXACK:
LOG_D("%s: Connection established, %s", peer->p_hdr.info.pi_diamid, fd_cnx_getid(cnx));
fd_p_ce_handle_newcnx(peer, cnx);
break;
default:
/* Just abort the attempt and continue */
TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing... (too slow?)", STATE_STR(cur_state));
fd_cnx_destroy(cnx);
}
goto psm_loop;
}
/* A new connection has not been established with the remote peer */
if (event == FDEVP_CNX_FAILED) {
/* 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;
switch (cur_state) {
case STATE_WAITCNXACK_ELEC:
/* Abort the initiating side */
fd_p_cnx_abort(peer, 0);
/* Process the receiver side */
CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
break;
case STATE_WAITCNXACK:
/* Go back to CLOSE */
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
goto psm_reset;
default:
/* Just ignore */
TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(cur_state));
}
goto psm_loop;
}
/* The timeout for the current state has been reached */
if (event == FDEVP_PSM_TIMEOUT) {
switch (cur_state) {
case STATE_OPEN:
case STATE_REOPEN:
case STATE_OPEN_NEW:
CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end );
goto psm_loop;
case STATE_CLOSED:
LOG_D("%s: Connecting...", peer->p_hdr.info.pi_diamid);
CHECK_FCT_DO( fd_psm_change_state(peer, STATE_WAITCNXACK), goto psm_end );
fd_psm_next_timeout(peer, 0, CNX_TIMEOUT);
CHECK_FCT_DO( fd_p_cnx_init(peer), goto psm_end );
goto psm_loop;
case STATE_SUSPECT:
/* Mark the connection problem */
peer->p_flags.pf_cnx_pb = 1;
case STATE_WAITCNXACK:
case STATE_WAITCEA:
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "Timeout while waiting for remote peer", NULL);
case STATE_CLOSING:
/* Destroy the connection, restart the timer to a new connection attempt */
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
goto psm_reset;
case STATE_CLOSING_GRACE:
/* The grace period is completed, now close */
if (peer->p_flags.pf_localterm)
goto psm_end;
fd_psm_cleanup(peer, 0);
/* Reset the timer for next connection attempt */
fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
goto psm_loop;
case STATE_WAITCNXACK_ELEC:
/* Abort the initiating side */
fd_p_cnx_abort(peer, 0);
/* Process the receiver side */
CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
goto psm_loop;
default:
ASSERT(0); /* implementation problem, we did not foresee this case? */
}
}
/* Default action : the handling has not yet been implemented. [for debug only] */
TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(cur_state), fd_pev_str(event));
psm_reset:
if (peer->p_flags.pf_delete)
goto psm_end;
fd_psm_cleanup(peer, 0);
goto psm_loop;
psm_end:
LOG_N("%s: Going to ZOMBIE state (no more activity)", peer->p_hdr.info.pi_diamid);
fd_psm_cleanup(peer, 1);
TRACE_DEBUG(INFO, "'%s'\t-> STATE_ZOMBIE (terminated)\t'%s'",
STATE_STR(fd_peer_getstate(peer)),
peer->p_hdr.info.pi_diamid);
pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
peer->p_psm = (pthread_t)NULL;
pthread_detach(pthread_self());
return NULL;
}
/************************************************************************/
/* Functions to control the PSM */
/************************************************************************/
/* Create the PSM thread of one peer structure */
int fd_psm_begin(struct fd_peer * peer )
{
TRACE_ENTRY("%p", peer);
/* Check the peer and state are OK */
CHECK_PARAMS( fd_peer_getstate(peer) == STATE_NEW );
/* Create the FIFO for events */
CHECK_FCT( fd_fifo_new(&peer->p_events, 0) );
/* Create the PSM controler thread */
CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) );
/* We're done */
return 0;
}
/* End the PSM (clean ending) */
int fd_psm_terminate(struct fd_peer * peer, char * reason )
{
TRACE_ENTRY("%p", peer);
CHECK_PARAMS( CHECK_PEER(peer) );
if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, reason) );
} else {
TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
}
return 0;
}
/* End the PSM & cleanup the peer structure */
void fd_psm_abord(struct fd_peer * peer )
{
TRACE_ENTRY("%p", peer);
/* Cancel PSM thread */
CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ );
/* Cleanup the data */
fd_psm_cleanup(peer, 1);
/* Destroy the event list */
CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
/* Remaining cleanups are performed in fd_peer_free */
return;
}
+350
View File
@@ -0,0 +1,350 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* Structure to store a sent request */
struct sentreq {
struct fd_list chain; /* the "o" field points directly to the (new) hop-by-hop of the request (uint32_t *) */
struct msg *req; /* A request that was sent and not yet answered. */
uint32_t prevhbh;/* The value to set back in the hbh header when the message is retrieved */
struct fd_list expire; /* the list of expiring requests */
struct timespec timeout; /* Cache the expire date of the request so that the timeout thread does not need to get it each time. */
struct timespec added_on; /* the time the request was added */
};
/* Find an element in the hbh list, or the following one */
static struct fd_list * find_or_next(struct fd_list * srlist, uint32_t hbh, int * match)
{
struct fd_list * li;
*match = 0;
for (li = srlist->next; li != srlist; li = li->next) {
uint32_t * nexthbh = li->o;
if (*nexthbh < hbh)
continue;
if (*nexthbh == hbh)
*match = 1;
break;
}
return li;
}
/* Similar but start from the end, since we add requests in growing hbh order usually */
static struct fd_list * find_or_prev(struct fd_list * srlist, uint32_t hbh, int * match)
{
struct fd_list * li;
*match = 0;
for (li = srlist->prev; li != srlist; li = li->prev) {
uint32_t * prevhbh = li->o;
if (*prevhbh > hbh)
continue;
if (*prevhbh == hbh)
*match = 1;
break;
}
return li;
}
static void srl_dump(const char * text, struct fd_list * srlist)
{
struct fd_list * li;
struct timespec now;
LOG_D("%sSentReq list @%p:", text, srlist);
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), );
for (li = srlist->next; li != srlist; li = li->next) {
struct sentreq * sr = (struct sentreq *)li;
uint32_t * nexthbh = li->o;
LOG_D(" - Next req (hbh:0x%x, prev:0x%x): [since %ld.%06ld sec]", *nexthbh, sr->prevhbh,
(long)((now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_sec - sr->added_on.tv_sec) : (now.tv_sec - sr->added_on.tv_sec - 1)),
(long)((now.tv_nsec >= sr->added_on.tv_nsec) ? ((now.tv_nsec - sr->added_on.tv_nsec) / 1000) : ((now.tv_nsec - sr->added_on.tv_nsec + 1000000000) / 1000)));
}
}
/* thread that handles messages expiring. The thread is started only when needed */
static void * sr_expiry_th(void * arg) {
struct sr_list * srlist = arg;
TRACE_ENTRY("%p", arg);
CHECK_PARAMS_DO( arg, return NULL );
/* Set the thread name */
{
char buf[48];
snprintf(buf, sizeof(buf), "ReqExp/%s", ((struct fd_peer *)(srlist->exp.o))->p_hdr.info.pi_diamid);
fd_log_threadname ( buf );
}
do {
struct timespec now;
struct sentreq * first;
struct msg * request;
struct fd_peer * sentto;
void (*expirecb)(void *, DiamId_t, size_t, struct msg **);
void * data;
int no_error;
CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx), return NULL );
pthread_cleanup_push( fd_cleanup_mutex, &srlist->mtx );
loop:
no_error = 0;
/* Check if there are expiring requests available */
if (FD_IS_LIST_EMPTY(&srlist->exp)) {
/* Just wait for a change or cancelation */
CHECK_POSIX_DO( pthread_cond_wait( &srlist->cnd, &srlist->mtx ), goto unlock );
/* Restart the loop on wakeup */
goto loop;
}
/* Get the pointer to the request that expires first */
first = (struct sentreq *)(srlist->exp.next->o);
/* Get the current time */
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), goto unlock );
/* If first request is not expired, we just wait until it happens */
if ( TS_IS_INFERIOR( &now, &first->timeout ) ) {
CHECK_POSIX_DO2( pthread_cond_timedwait( &srlist->cnd, &srlist->mtx, &first->timeout ),
ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
/* on other error, */ goto unlock );
/* on wakeup, loop */
goto loop;
}
/* Now, the first request in the list is expired; remove it and call the expirecb for it */
request = first->req;
sentto = first->chain.head->o;
TRACE_DEBUG(FULL, "Request %x was not answered by %s within the timer delay", *((uint32_t *)first->chain.o), sentto->p_hdr.info.pi_diamid);
/* Restore the hbhid */
*((uint32_t *)first->chain.o) = first->prevhbh;
/* Free the sentreq information */
fd_list_unlink(&first->chain);
srlist->cnt--;
srlist->cnt_lost++; /* We are not waiting for this answer anymore, but the remote peer may still be processing it. */
fd_list_unlink(&first->expire);
free(first);
no_error = 1;
unlock:
; /* pthread_cleanup_pop sometimes expands as "} ..." and the label before this cause some compilers to complain... */
pthread_cleanup_pop( 1 ); /* unlock the mutex */
if (!no_error)
break;
/* Retrieve callback in the message */
CHECK_FCT_DO( fd_msg_anscb_get( request, NULL, &expirecb, &data ), break);
ASSERT(expirecb);
/* Clean up this expirecb from the message */
CHECK_FCT_DO( fd_msg_anscb_reset( request, 0, 1 ), break);
/* Call it */
(*expirecb)(data, sentto->p_hdr.info.pi_diamid, sentto->p_hdr.info.pi_diamidlen, &request);
/* If the callback did not dispose of the message, do it now */
if (request) {
fd_hook_call(HOOK_MESSAGE_DROPPED, request, NULL, "Expiration period completed without an answer, and the expiry callback did not dispose of the message.", fd_msg_pmdl_get(request));
CHECK_FCT_DO( fd_msg_free(request), /* ignore */ );
}
} while (1);
ASSERT(0); /* we have encountered a problem, maybe time to signal the framework to terminate? */
return NULL;
}
/* Store a new sent request */
int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore)
{
struct sentreq * sr;
struct fd_list * prev;
int match;
struct timespec * ts;
TRACE_ENTRY("%p %p %p %x", srlist, req, hbhloc, hbh_restore);
CHECK_PARAMS(srlist && req && *req && hbhloc);
CHECK_MALLOC( sr = malloc(sizeof(struct sentreq)) );
memset(sr, 0, sizeof(struct sentreq));
fd_list_init(&sr->chain, hbhloc);
sr->req = *req;
sr->prevhbh = hbh_restore;
fd_list_init(&sr->expire, sr);
CHECK_SYS( clock_gettime(CLOCK_REALTIME, &sr->added_on) );
/* Search the place in the list */
CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
prev = find_or_prev(&srlist->srs, *hbhloc, &match);
if (match) {
TRACE_DEBUG(INFO, "A request with the same hop-by-hop Id (0x%x) was already sent: error", *hbhloc);
free(sr);
srl_dump("Current list of SR: ", &srlist->srs);
CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* ignore */ );
return EINVAL;
}
/* Save in the list */
*req = NULL;
fd_list_insert_after(prev, &sr->chain);
srlist->cnt++;
/* In case of request with a timeout, also store in the timeout list */
ts = fd_msg_anscb_gettimeout( sr->req );
if (ts) {
struct fd_list * li;
memcpy(&sr->timeout, ts, sizeof(struct timespec));
/* browse srlist->exp from the end */
for (li = srlist->exp.prev; li != &srlist->exp; li = li->prev) {
struct sentreq * s = (struct sentreq *)(li->o);
if (TS_IS_INFERIOR(&s->timeout, ts))
break;
}
fd_list_insert_after(li, &sr->expire);
/* if the thread does not exist yet, create it */
if (srlist->thr == (pthread_t)NULL) {
CHECK_POSIX_DO( pthread_create(&srlist->thr, NULL, sr_expiry_th, srlist), /* continue anyway */);
} else {
/* or, if added in first position, signal the condvar to update the sleep time of the thread */
if (li == &srlist->exp) {
CHECK_POSIX_DO( pthread_cond_signal(&srlist->cnd), /* continue anyway */);
}
}
}
CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
return 0;
}
/* Fetch a request by hbh */
int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req)
{
struct sentreq * sr;
int match;
TRACE_ENTRY("%p %x %p", srlist, hbh, req);
CHECK_PARAMS(srlist && req);
/* Search the request in the list */
CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
sr = (struct sentreq *)find_or_next(&srlist->srs, hbh, &match);
if (!match) {
TRACE_DEBUG(INFO, "There is no saved request with this hop-by-hop id (%x)", hbh);
srl_dump("Current list of SR: ", &srlist->srs);
*req = NULL;
if (srlist->cnt_lost > 0) {
srlist->cnt_lost--; /* This is probably an answer for a request we already timedout. */
} /* else, probably a bug in the remote peer */
} else {
/* Restore hop-by-hop id */
*((uint32_t *)sr->chain.o) = sr->prevhbh;
/* Unlink */
fd_list_unlink(&sr->chain);
srlist->cnt--;
fd_list_unlink(&sr->expire);
*req = sr->req;
free(sr);
}
CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
/* do not stop the expire thread here, it might cause creating/destroying it very often otherwise */
/* Done */
return 0;
}
/* Failover requests (free or requeue routables) */
void fd_p_sr_failover(struct sr_list * srlist)
{
CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx), /* continue anyway */ );
while (!FD_IS_LIST_EMPTY(&srlist->srs)) {
struct sentreq * sr = (struct sentreq *)(srlist->srs.next);
fd_list_unlink(&sr->chain);
srlist->cnt--;
fd_list_unlink(&sr->expire);
if (fd_msg_is_routable(sr->req)) {
struct msg_hdr * hdr = NULL;
int ret;
/* Set the 'T' flag */
CHECK_FCT_DO(fd_msg_hdr(sr->req, &hdr), /* continue */);
if (hdr)
hdr->msg_flags |= CMD_FLAG_RETRANSMIT;
/* Restore the original hop-by-hop id of the request */
*((uint32_t *)sr->chain.o) = sr->prevhbh;
fd_hook_call(HOOK_MESSAGE_FAILOVER, sr->req, (struct fd_peer *)srlist->srs.o, NULL, fd_msg_pmdl_get(sr->req));
/* Requeue for sending to another peer */
CHECK_FCT_DO( ret = fd_fifo_post_noblock(fd_g_outgoing, (void *)&sr->req),
{
char buf[256];
snprintf(buf, sizeof(buf), "Internal error: error while requeuing during failover: %s", strerror(ret));
fd_hook_call(HOOK_MESSAGE_DROPPED, sr->req, NULL, buf, fd_msg_pmdl_get(sr->req));
CHECK_FCT_DO(fd_msg_free(sr->req), /* What can we do more? */)
});
} else {
/* Just free the request. */
/* fd_hook_call(HOOK_MESSAGE_DROPPED, sr->req, NULL, "Sent & unanswered local message discarded during failover.", fd_msg_pmdl_get(sr->req)); */
CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */);
}
free(sr);
}
/* The list of expiring requests must be empty now */
ASSERT( FD_IS_LIST_EMPTY(&srlist->exp) );
ASSERT( srlist->cnt == 0 ); /* debug the counter management if needed */
CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* continue anyway */ );
/* Terminate the expiry thread (must be done when the lock can be taken) */
CHECK_FCT_DO( fd_thr_term(&srlist->thr), /* ignore error */ );
}
+700
View File
@@ -0,0 +1,700 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* Global list of peers */
struct fd_list fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers);
pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
/* List of active peers */
struct fd_list fd_g_activ_peers = FD_LIST_INITIALIZER(fd_g_activ_peers); /* peers linked by their p_actives oredered by p_diamid */
pthread_rwlock_t fd_g_activ_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
/* List of validation callbacks (registered with fd_peer_validate_register) */
static struct fd_list validators = FD_LIST_INITIALIZER(validators); /* list items are simple fd_list with "o" pointing to the callback */
static pthread_rwlock_t validators_rw = PTHREAD_RWLOCK_INITIALIZER;
/* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
int fd_peer_alloc(struct fd_peer ** ptr)
{
struct fd_peer *p;
TRACE_ENTRY("%p", ptr);
CHECK_PARAMS(ptr);
if (*ptr) {
p = *ptr;
} else {
CHECK_MALLOC( p = malloc(sizeof(struct fd_peer)) );
*ptr = p;
}
/* Now initialize the content */
memset(p, 0, sizeof(struct fd_peer));
fd_list_init(&p->p_hdr.chain, p);
fd_list_init(&p->p_hdr.info.pi_endpoints, p);
fd_list_init(&p->p_hdr.info.runtime.pir_apps, p);
p->p_eyec = EYEC_PEER;
CHECK_POSIX( pthread_mutex_init(&p->p_state_mtx, NULL) );
fd_list_init(&p->p_actives, p);
fd_list_init(&p->p_expiry, p);
CHECK_FCT( fd_fifo_new(&p->p_tosend, 1024) );// initial is 5
CHECK_FCT( fd_fifo_new(&p->p_tofailover, 0) );
p->p_hbh = lrand48();
fd_list_init(&p->p_sr.srs, p);
fd_list_init(&p->p_sr.exp, p);
CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) );
CHECK_POSIX( pthread_cond_init(&p->p_sr.cnd, NULL) );
fd_list_init(&p->p_connparams, p);
return 0;
}
/* Add a new peer entry */
int fd_peer_add ( struct peer_info * info, const char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
{
struct fd_peer *p = NULL;
struct fd_list * li, *li_inf;
int ret = 0;
TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
CHECK_PARAMS(info && info->pi_diamid);
if (info->config.pic_realm) {
if (!fd_os_is_valid_DiameterIdentity((os0_t)info->config.pic_realm, strlen(info->config.pic_realm))) {
TRACE_DEBUG(INFO, "'%s' is not a valid DiameterIdentity.", info->config.pic_realm);
return EINVAL;
}
}
/* Create a structure to contain the new peer information */
CHECK_FCT( fd_peer_alloc(&p) );
/* Copy the informations from the parameters received */
p->p_hdr.info.pi_diamid = info->pi_diamid;
CHECK_FCT( fd_os_validate_DiameterIdentity(&p->p_hdr.info.pi_diamid, &p->p_hdr.info.pi_diamidlen, 1) );
memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) );
/* Duplicate the strings if provided */
if (info->config.pic_realm) {
CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) );
}
if (info->config.pic_priority) {
CHECK_MALLOC( p->p_hdr.info.config.pic_priority = strdup(info->config.pic_priority) );
}
/* Move the list of endpoints into the peer */
if (info->pi_endpoints.next)
while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
li = info->pi_endpoints.next;
fd_list_unlink(li);
fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
}
/* The internal data */
if (orig_dbg) {
CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
} else {
CHECK_MALLOC( p->p_dbgorig = strdup("unspecified") );
}
p->p_cb = cb;
p->p_cb_data = cb_data;
/* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */
CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
li_inf = &fd_g_peers;
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * next = (struct fd_peer *)li;
int cont;
int cmp = fd_os_almostcasesrch( p->p_hdr.info.pi_diamid, p->p_hdr.info.pi_diamidlen,
next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen,
&cont );
if (cmp > 0)
li_inf = li; /* it will come after this element, for sure */
if (cmp == 0) {
ret = EEXIST; /* we have a duplicate */
break;
}
if (!cont)
break;
}
/* We can insert the new peer object */
if (! ret)
do {
/* Update expiry list */
CHECK_FCT_DO( ret = fd_p_expi_update( p ), break );
/* Insert the new element in the list */
fd_list_insert_after( li_inf, &p->p_hdr.chain );
} while (0);
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
if (ret) {
CHECK_FCT( fd_peer_free(&p) );
} else {
CHECK_FCT( fd_psm_begin(p) );
}
return ret;
}
/* Search for a peer */
int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer )
{
struct fd_list * li;
TRACE_ENTRY("%p %zd %d %p", diamid, diamidlen, igncase, peer);
CHECK_PARAMS( diamid && diamidlen && peer );
*peer = NULL;
/* Search in the list */
CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) );
if (igncase) {
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * next = (struct fd_peer *)li;
int cmp, cont;
cmp = fd_os_almostcasesrch( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen, &cont );
if (cmp == 0) {
*peer = &next->p_hdr;
break;
}
if (!cont)
break;
}
} else {
LOG_D("fd_peer_getbyid enter");
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * next = (struct fd_peer *)li;
LOG_D("fd_peer_getbyid src[%u,%s] peer[%u,%s]",
(unsigned int)diamidlen,
diamid,
(unsigned int)( next->p_hdr.info.pi_diamidlen ),
next->p_hdr.info.pi_diamid);
int cmp = fd_os_cmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen );
if (cmp > 0)
continue;
if (cmp == 0)
*peer = &next->p_hdr;
break;
}
}
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
return 0;
}
#define free_null( _v ) \
if (_v) { \
free(_v); \
(_v) = NULL; \
}
#define free_list( _l ) \
while (!FD_IS_LIST_EMPTY(_l)) { \
struct fd_list * __li = ((struct fd_list *)(_l))->next; \
fd_list_unlink(__li); \
free(__li); \
}
/* Empty the lists of p_tosend, p_failover, and p_sentreq messages */
void fd_peer_failover_msg(struct fd_peer * peer)
{
struct msg *m;
TRACE_ENTRY("%p", peer);
CHECK_PARAMS_DO(CHECK_PEER(peer), return);
/* Requeue all messages in the "out" queue */
while ( fd_fifo_tryget(peer->p_tosend, &m) == 0 ) {
/* but only if they are routable */
if (fd_msg_is_routable(m)) {
fd_hook_call(HOOK_MESSAGE_FAILOVER, m, peer, NULL, fd_msg_pmdl_get(m));
CHECK_FCT_DO(fd_fifo_post_noblock(fd_g_outgoing, (void *)&m),
{
/* fallback: destroy the message */
fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Internal error: unable to requeue this message during failover process", fd_msg_pmdl_get(m));
CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
} );
} else {
/* Just free it */
/* fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Non-routable message freed during handover", fd_msg_pmdl_get(m)); */
CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
}
}
/* Requeue all messages in the "failover" queue */
while ( fd_fifo_tryget(peer->p_tofailover, &m) == 0 ) {
fd_hook_call(HOOK_MESSAGE_FAILOVER, m, peer, NULL, fd_msg_pmdl_get(m));
CHECK_FCT_DO(fd_fifo_post_noblock(fd_g_outgoing, (void *)&m),
{
/* fallback: destroy the message */
fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Internal error: unable to requeue this message during failover process", fd_msg_pmdl_get(m));
CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
} );
}
/* Requeue all routable sent requests */
fd_p_sr_failover(&peer->p_sr);
/* Done */
return;
}
/* Describe the current connection */
int fd_peer_cnx_proto_info(struct peer_hdr *peer, char * buf, size_t len)
{
struct fd_peer * p = (struct fd_peer *)peer;
TRACE_ENTRY("%p %p %zd", peer, buf, len);
CHECK_PARAMS(CHECK_PEER(peer) && buf && len);
if (p->p_cnxctx) {
CHECK_FCT(fd_cnx_proto_info(p->p_cnxctx, buf, len));
} else if (p->p_receiver) {
CHECK_FCT(fd_cnx_proto_info(p->p_receiver, buf, len));
} else {
snprintf(buf, len, "Not Connected");
}
return 0;
}
/* Return the value of srlist->cnt */
int fd_peer_get_load_pending(struct peer_hdr *peer, long * to_receive, long * to_send)
{
struct fd_peer * p = (struct fd_peer *)peer;
TRACE_ENTRY("%p %p %p", peer, to_receive, to_send);
CHECK_PARAMS(CHECK_PEER(peer));
if (to_receive) {
CHECK_POSIX( pthread_mutex_lock(&p->p_sr.mtx) );
*to_receive = p->p_sr.cnt;
CHECK_POSIX( pthread_mutex_unlock(&p->p_sr.mtx) );
}
if (to_send) {
CHECK_POSIX( pthread_mutex_lock(&p->p_state_mtx) );
*to_send = p->p_reqin_count;
CHECK_POSIX( pthread_mutex_unlock(&p->p_state_mtx) );
}
return 0;
}
/* Destroy a structure once cleanups have been performed (fd_psm_abord, ...) */
int fd_peer_free(struct fd_peer ** ptr)
{
struct fd_peer *p;
TRACE_ENTRY("%p", ptr);
CHECK_PARAMS(ptr);
p = *ptr;
*ptr = NULL;
CHECK_PARAMS(p);
CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
free_null(p->p_hdr.info.pi_diamid);
free_null(p->p_hdr.info.config.pic_realm);
free_null(p->p_hdr.info.config.pic_priority);
free_null(p->p_hdr.info.runtime.pir_realm);
free_null(p->p_hdr.info.runtime.pir_prodname);
free_list( &p->p_hdr.info.runtime.pir_apps );
free_list( &p->p_hdr.info.pi_endpoints );
free_null(p->p_dbgorig);
fd_list_unlink(&p->p_expiry);
fd_list_unlink(&p->p_actives);
CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
CHECK_FCT_DO( fd_fifo_del(&p->p_tofailover), /* continue */ );
CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_state_mtx), /* continue */);
CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */);
/* If the callback is still around... */
if (p->p_cb)
(*p->p_cb)(NULL, p->p_cb_data);
/* Free the structure */
free(p);
return 0;
}
/* Terminate peer module (destroy all peers, first gently, then violently) */
int fd_peer_fini()
{
struct fd_list * li;
struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
int list_empty;
struct timespec wait_until, now;
TRACE_ENTRY();
CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");
CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * peer = (struct fd_peer *)li->o;
if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ );
} else {
li = li->prev; /* to avoid breaking the loop */
fd_list_unlink(&peer->p_hdr.chain);
fd_list_insert_before(&purge, &peer->p_hdr.chain);
}
}
list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
if (!list_empty) {
CHECK_SYS( clock_gettime(CLOCK_REALTIME, &now) );
fd_psm_start(); /* just in case */
TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT + 1);
wait_until.tv_sec = now.tv_sec + DPR_TIMEOUT + 1;
wait_until.tv_nsec = now.tv_nsec;
}
while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
/* Allow the PSM(s) to execute */
usleep(100000);
/* Remove zombie peers */
CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * peer = (struct fd_peer *)li->o;
if (fd_peer_getstate(peer) == STATE_ZOMBIE) {
li = li->prev; /* to avoid breaking the loop */
fd_list_unlink(&peer->p_hdr.chain);
fd_list_insert_before(&purge, &peer->p_hdr.chain);
}
}
list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
CHECK_SYS( clock_gettime(CLOCK_REALTIME, &now) );
}
if (!list_empty) {
TRACE_DEBUG(INFO, "Forcing connections shutdown");
CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
while (!FD_IS_LIST_EMPTY(&fd_g_peers)) {
struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next->o);
fd_psm_abord(peer);
fd_list_unlink(&peer->p_hdr.chain);
fd_list_insert_before(&purge, &peer->p_hdr.chain);
}
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
}
/* Free memory objects of all peers */
while (!FD_IS_LIST_EMPTY(&purge)) {
struct fd_peer * peer = (struct fd_peer *)(purge.next->o);
fd_list_unlink(&peer->p_hdr.chain);
fd_peer_free(&peer);
}
/* Now empty the validators list */
CHECK_FCT_DO( pthread_rwlock_wrlock(&validators_rw), /* continue */ );
while (!FD_IS_LIST_EMPTY( &validators )) {
struct fd_list * v = validators.next;
fd_list_unlink(v);
free(v);
}
CHECK_FCT_DO( pthread_rwlock_unlock(&validators_rw), /* continue */ );
return 0;
}
/* Dump info of one peer */
DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump, struct peer_hdr * p, int details)
{
FD_DUMP_HANDLE_OFFSET();
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{peer}(@%p): ", p), return NULL);
if (!CHECK_PEER(p)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
} else {
struct fd_peer * peer = (struct fd_peer *)p;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s [%s, cnt:%ldsr,%ldpa]", peer->p_hdr.info.pi_diamid, STATE_STR(fd_peer_getstate(peer)), peer->p_sr.cnt, peer->p_reqin_count), return NULL);
if (details > 0) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " rlm:%s", peer->p_hdr.info.runtime.pir_realm ?: "<unknown>"), return NULL);
if (peer->p_hdr.info.runtime.pir_prodname) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " ['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev), return NULL);
}
}
if (details > 1) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " [from:%s] flags:%s%s%s%s%s%s%s%s lft:%ds",
peer->p_dbgorig ?: "unset",
peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_DEFAULT ? "-" :
(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP ? "4" : "6"),
peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_DEFAULT ? "-" :
(peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_TCP ? "T" : "S"),
peer->p_hdr.info.config.pic_flags.alg ? "P" : "-",
peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE ? "N" :"-",
peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD ? "O" :"-",
peer->p_hdr.info.config.pic_flags.sctpsec & PI_SCTPSEC_3436 ? "3" :"-",
peer->p_hdr.info.config.pic_flags.exp ? "E" : "-",
peer->p_hdr.info.config.pic_flags.persist ? "P" : "-",
peer->p_hdr.info.config.pic_lft), return NULL);
}
}
return *buf;
}
/* Dump the list of peers */
DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump_list, int details)
{
struct fd_list * li;
FD_DUMP_HANDLE_OFFSET();
CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
CHECK_MALLOC_DO( fd_peer_dump(FD_DUMP_STD_PARAMS, (struct peer_hdr *)li->o, details), break);
if (li->next != &fd_g_peers) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), break);
}
}
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
return *buf;
}
static struct dict_object *avp_oh_model = NULL;
static pthread_mutex_t cache_avp_lock = PTHREAD_MUTEX_INITIALIZER;
/* Handle an incoming CER request on a new connection */
int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx )
{
struct msg * msg;
struct avp *avp_oh;
struct avp_hdr * avp_hdr;
struct fd_list * li, *li_inf;
int found = 0;
int ret = 0;
struct fd_peer * peer;
struct cnx_incoming * ev_data;
TRACE_ENTRY("%p %p", cer, cnx);
CHECK_PARAMS(cer && *cer && cnx && *cnx);
msg = *cer;
/* If needed, resolve the dictionary model for Origin-Host */
CHECK_POSIX( pthread_mutex_lock(&cache_avp_lock) );
if (!avp_oh_model) {
avp_code_t code = AC_ORIGIN_HOST;
CHECK_FCT_DO( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT),
{ LOG_E("Cannot find Origin-Host AVP definition in the dictionary!"); (void) pthread_mutex_unlock(&cache_avp_lock); return __ret__; } );
}
CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) );
/* Find the Diameter Identity of the remote peer in the message */
CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) );
ASSERT(avp_oh); /* otherwise it should not have passed rules validation, right? */
CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
/* First, check if the Origin-Host value is valid */
if (!fd_os_is_valid_DiameterIdentity(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len)) {
CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ) );
CHECK_FCT( fd_msg_rescode_set(*cer, "DIAMETER_INVALID_AVP_VALUE",
"Your Origin-Host contains invalid characters.", avp_oh, 1 ) );
fd_hook_call(HOOK_PEER_CONNECT_FAILED, *cer, NULL, "Received CER with invalid Origin-Host AVP", NULL);
CHECK_FCT( fd_out_send(cer, *cnx, NULL, 0) );
return EINVAL;
}
/* Search if we already have this peer id in our list. We take directly the write lock so that we don't need to upgrade if it is a new peer.
* There is space for a small optimization here if needed.
*/
CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
li_inf = &fd_g_peers;
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
int cmp, cont;
peer = (struct fd_peer *)li;
cmp = fd_os_almostcasesrch( avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, &cont );
if (cmp > 0) {
li_inf = li;
}
if (cmp == 0) {
found = 1;
break;
}
if (!cont)
break;
}
if (!found) {
/* Create a new peer entry for this new remote peer */
peer = NULL;
CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out );
/* Set the peer Diameter Id and the responder flag parameters */
CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = os0dup(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len),
{ ret = ENOMEM; goto out; } );
peer->p_hdr.info.pi_diamidlen = avp_hdr->avp_value->os.len;
CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } );
peer->p_flags.pf_responder = 1;
peer->p_flags.pf_delete = 1;
LOG_D("Created new peer object for incoming CER: %s", peer->p_hdr.info.pi_diamid);
#ifndef DISABLE_PEER_EXPIRY
/* Set this peer to expire on inactivity */
peer->p_hdr.info.config.pic_flags.exp = PI_EXP_INACTIVE;
peer->p_hdr.info.config.pic_lft = 3600; /* 1 hour without any message
-- RFC3539 states that this must not be inferior to BRINGDOWN_INTERVAL = 5 minutes */
CHECK_FCT_DO( ret = fd_p_expi_update( peer ), goto out );
#endif /* DISABLE_PEER_EXPIRY */
/* Insert the new peer in the list (the PSM will take care of setting the expiry after validation) */
fd_list_insert_after( li_inf, &peer->p_hdr.chain );
/* Start the PSM, which will receive the event below */
CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
} else {
/* Check if the peer is in zombie state */
if (fd_peer_getstate(peer) == STATE_ZOMBIE) {
/* Re-activate the peer */
if (peer->p_hdr.info.config.pic_flags.exp)
peer->p_flags.pf_responder = 1;
CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
peer->p_state = STATE_NEW;
CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
peer->p_flags.pf_localterm = 0;
CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
}
}
/* Send the new connection event to the PSM */
CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } );
memset(ev_data, 0, sizeof(*ev_data));
ev_data->cer = msg;
ev_data->cnx = *cnx;
ev_data->validate = !found;
CHECK_FCT_DO( ret = fd_event_send(peer->p_events, FDEVP_CNX_INCOMING, sizeof(*ev_data), ev_data), goto out );
out:
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
if (ret == 0) {
/* Reset the "out" parameters, so that they are not cleanup on function return. */
*cer = NULL;
*cnx = NULL;
} else {
char buf[1024];
snprintf(buf, sizeof(buf), "An error occurred while processing new incoming CER: %s", strerror(ret));
fd_hook_call(HOOK_PEER_CONNECT_FAILED, *cer, NULL, buf, NULL);
}
return ret;
}
/* Save a callback to accept / reject incoming unknown peers */
int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) )
{
struct fd_list * v;
TRACE_ENTRY("%p", peer_validate);
CHECK_PARAMS(peer_validate);
/* Alloc a new entry */
CHECK_MALLOC( v = malloc(sizeof(struct fd_list)) );
fd_list_init( v, peer_validate );
/* Add at the beginning of the list */
CHECK_FCT( pthread_rwlock_wrlock(&validators_rw) );
fd_list_insert_after(&validators, v);
CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
/* Done! */
return 0;
}
/* Validate a peer by calling the callbacks in turn -- return 0 if the peer is validated, ! 0 in case of error (>0) or if the peer is rejected (-1) */
int fd_peer_validate( struct fd_peer * peer )
{
int ret = 0;
struct fd_list * v;
CHECK_FCT( pthread_rwlock_rdlock(&validators_rw) );
for (v = validators.next; v != &validators; v = v->next) {
int auth = 0;
pthread_cleanup_push(fd_cleanup_rwlock, &validators_rw);
CHECK_FCT_DO( ret = ((int(*)(struct peer_info *, int *, int (**)(struct peer_info *)))(v->o)) (&peer->p_hdr.info, &auth, &peer->p_cb2), );
pthread_cleanup_pop(0);
if (ret)
goto out;
if (auth) {
ret = (auth > 0) ? 0 : -1;
goto out;
}
peer->p_cb2 = NULL;
}
/* No callback has given a firm result, the default is to reject */
ret = -1;
ret = 0;
out:
CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
return ret;
}
+86
View File
@@ -0,0 +1,86 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* The global message queues */
struct fifo * fd_g_incoming = NULL;
struct fifo * fd_g_outgoing = NULL;
struct fifo * fd_g_local = NULL;
/* Initialize the message queues. */
#define FD_MSG_QUEUE_SIZE 2048
int fd_queues_init(void)
{
TRACE_ENTRY();
CHECK_FCT( fd_fifo_new ( &fd_g_incoming, FD_MSG_QUEUE_SIZE ) );
CHECK_FCT( fd_fifo_new ( &fd_g_outgoing, FD_MSG_QUEUE_SIZE ) );
CHECK_FCT( fd_fifo_new ( &fd_g_local, FD_MSG_QUEUE_SIZE ) );
return 0;
}
/* Destroy a queue after emptying it (and dumping the content) */
int fd_queues_fini(struct fifo ** queue)
{
struct msg * msg;
int ret = 0;
TRACE_ENTRY("%p", queue);
/* Note : the threads that post into this queue should already been stopped before this !!! */
CHECK_PARAMS(queue);
if (*queue == NULL)
return 0; /* the queue was not already initialized */
/* Empty all contents */
while (1) {
/* Check if there is a message in the queue */
ret = fd_fifo_tryget(*queue, &msg);
if (ret == EWOULDBLOCK)
break;
CHECK_FCT(ret);
/* We got one! */
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, "Message lost because framework is terminating.", fd_msg_pmdl_get(msg));
fd_msg_free(msg);
}
/* Now, delete the empty queue */
CHECK_FCT( fd_fifo_del ( queue ) );
return 0;
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+793
View File
@@ -0,0 +1,793 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
/* This file contains code for TLS over multi-stream SCTP wrapper implementation (GnuTLS does not support this) */
/* See http://aaa.koganei.wide.ad.jp/blogs/index.php/waaad/2008/08/18/tls-over-sctp for history */
#include "fdcore-internal.h"
#include "cnxctx.h"
#include <netinet/sctp.h>
#include <sys/uio.h>
/*
Architecture of this wrapper:
- we have several fifo queues (1 per stream pairs).
GnuTLS is configured to use custom push / pull functions:
- the pull function retrieves the data from the fifo queue corresponding to a stream #.
- the push function sends the data on a certain stream.
We also have a demux thread that reads the socket and store received data in the appropriate fifo
We have one gnutls_session per stream pair, and as many threads that read the gnutls records and save incoming data to the target queue.
This complexity is required because we cannot read a socket for a given stream only; we can only get the next message and find its stream.
*/
/* Note that this mechanism is replaced by DTLS in RFC6733 */
/*************************************************************/
/* threads */
/*************************************************************/
/* Demux received data and store in the appropriate fifo */
static void * demuxer(void * arg)
{
struct cnxctx * conn = arg;
uint8_t * buf;
size_t bufsz;
int event;
uint16_t strid;
TRACE_ENTRY("%p", arg);
CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
/* Set the thread name */
{
char buf[48];
snprintf(buf, sizeof(buf), "Demuxer (%d:%s)", conn->cc_socket, conn->cc_remid);
fd_log_threadname ( buf );
}
ASSERT( conn->cc_proto == IPPROTO_SCTP );
ASSERT( fd_cnx_target_queue(conn) );
ASSERT( conn->cc_sctp3436_data.array );
do {
CHECK_FCT_DO( fd_sctp_recvmeta(conn, &strid, &buf, &bufsz, &event), goto fatal );
switch (event) {
case FDEVP_CNX_MSG_RECV:
/* Demux this message to the appropriate fifo, another thread will pull, gnutls process, and send to target queue */
if (strid < conn->cc_sctp_para.pairs) {
CHECK_FCT_DO(fd_event_send(conn->cc_sctp3436_data.array[strid].raw_recv, event, bufsz, buf), goto fatal );
} else {
TRACE_DEBUG(INFO, "Received packet (%zd bytes) on out-of-range stream #%d from %s, discarded.", bufsz, strid, conn->cc_remid);
free(buf);
}
break;
case FDEVP_CNX_EP_CHANGE:
/* Send this event to the target queue */
CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal );
break;
case FDEVP_CNX_ERROR:
goto out;
case FDEVP_CNX_SHUTDOWN:
/* Just ignore the notification for now, we will get another error later anyway */
continue;
default:
goto fatal;
}
} while (conn->cc_loop);
out:
/* Signal termination of the connection to all decipher threads */
for (strid = 0; strid < conn->cc_sctp_para.pairs; strid++) {
if (conn->cc_sctp3436_data.array[strid].raw_recv) {
CHECK_FCT_DO(fd_event_send(conn->cc_sctp3436_data.array[strid].raw_recv, FDEVP_CNX_ERROR, 0, NULL), goto fatal );
}
}
fd_cnx_markerror(conn);
TRACE_DEBUG(FULL, "Thread terminated");
return NULL;
fatal:
/* An unrecoverable error occurred, stop the daemon */
CHECK_FCT_DO(fd_core_shutdown(), );
goto out;
}
/* Decrypt the data received in this stream pair and store it in the target queue */
static void * decipher(void * arg)
{
struct sctp3436_ctx * ctx = arg;
struct cnxctx *cnx;
TRACE_ENTRY("%p", arg);
CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error);
cnx = ctx->parent;
ASSERT( fd_cnx_target_queue(cnx) );
/* Set the thread name */
{
char buf[48];
snprintf(buf, sizeof(buf), "Decipher (%hu@%d:%s)", ctx->strid, cnx->cc_socket, cnx->cc_remid);
fd_log_threadname ( buf );
}
/* The next function loops while there is no error */
CHECK_FCT_DO(fd_tls_rcvthr_core(cnx, ctx->strid ? ctx->session : cnx->cc_tls_para.session), /* continue */);
error:
fd_cnx_markerror(cnx);
TRACE_DEBUG(FULL, "Thread terminated");
return NULL;
}
/*************************************************************/
/* push / pull */
/*************************************************************/
#ifdef GNUTLS_VERSION_300
/* Check if data is available for gnutls on a given context */
static int sctp3436_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms)
{
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
struct timespec tsstore, *ts = NULL;
int ret;
TRACE_ENTRY("%p %d", tr, ms);
if (ctx->partial.buf)
return 1; /* data is already available for pull */
if (ms) {
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &tsstore), return -1 );
tsstore.tv_nsec += (long)ms * 1000000;
tsstore.tv_sec += tsstore.tv_nsec / 1000000000L;
tsstore.tv_nsec %= 1000000000L;
ts = &tsstore;
}
ret = fd_fifo_select ( ctx->raw_recv, ts );
if (ret < 0) {
errno = -ret;
ret = -1;
}
return ret;
}
#endif /* GNUTLS_VERSION_300 */
/* Send data over the connection, called by gnutls */
#ifndef GNUTLS_VERSION_212
static ssize_t sctp3436_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
{
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
struct iovec iov;
TRACE_ENTRY("%p %p %zd", tr, data, len);
CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } );
iov.iov_base = (void *)data;
iov.iov_len = len;
return fd_sctp_sendstrv(ctx->parent, ctx->strid, &iov, 1);
}
#else /* GNUTLS_VERSION_212 */
static ssize_t sctp3436_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt)
{
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
TRACE_ENTRY("%p %p %d", tr, iov, iovcnt);
CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } );
return fd_sctp_sendstrv(ctx->parent, ctx->strid, (const struct iovec *)iov, iovcnt);
}
#endif /* GNUTLS_VERSION_212 */
/* Retrieve data received on a stream and already demultiplexed */
static ssize_t sctp3436_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
{
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
size_t pulled = 0;
int emptied;
TRACE_ENTRY("%p %p %zd", tr, buf, len);
CHECK_PARAMS_DO( tr && buf, { errno = EINVAL; goto error; } );
/* If we don't have data available now, pull new message from the fifo -- this is blocking (until the queue is destroyed) */
if (!ctx->partial.buf) {
int ev;
CHECK_FCT_DO( errno = fd_event_get(ctx->raw_recv, &ev, &ctx->partial.bufsz, (void *)&ctx->partial.buf), goto error );
if (ev == FDEVP_CNX_ERROR) {
/* Documentations says to return 0 on connection closed, but it does hang within gnutls_handshake */
return -1;
}
}
pulled = ctx->partial.bufsz - ctx->partial.offset;
if (pulled <= len) {
emptied = 1;
} else {
/* limit to the capacity of destination buffer */
emptied = 0;
pulled = len;
}
/* Store the data in the destination buffer */
memcpy(buf, ctx->partial.buf + ctx->partial.offset, pulled);
/* Free the buffer if we read all its content, and reset the partial structure */
if (emptied) {
free(ctx->partial.buf);
memset(&ctx->partial, 0, sizeof(ctx->partial));
} else {
ctx->partial.offset += pulled;
}
/* We are done */
return pulled;
error:
gnutls_transport_set_errno (ctx->session, errno);
return -1;
}
/* Set the parameters of a session to use the appropriate fifo and stream information */
#ifndef GNUTLS_VERSION_300
GCC_DIAG_OFF("-Wdeprecated-declarations")
#endif /* !GNUTLS_VERSION_300 */
static void set_sess_transport(gnutls_session_t session, struct sctp3436_ctx *ctx)
{
/* Set the transport pointer passed to push & pull callbacks */
GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) ctx ) );
/* Reset the low water value, since we don't use sockets */
#ifndef GNUTLS_VERSION_300
/* starting version 2.12, this call is not needed */
//GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
#else /* GNUTLS_VERSION_300 */
/* but in 3.0 we have to provide the pull_timeout callback */
GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp3436_pull_timeout ) );
#endif /* GNUTLS_VERSION_300 */
/* Set the push and pull callbacks */
GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp3436_pull) );
#ifndef GNUTLS_VERSION_212
GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp3436_push) );
#else /* GNUTLS_VERSION_212 */
GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp3436_pushv) );
#endif /* GNUTLS_VERSION_212 */
return;
}
#ifndef GNUTLS_VERSION_300
GCC_DIAG_ON("-Wdeprecated-declarations")
#endif /* !GNUTLS_VERSION_300 */
/*************************************************************/
/* Session resuming support */
/*************************************************************/
struct sr_store {
struct fd_list list; /* list of sr_data, ordered by key.size then key.data */
pthread_rwlock_t lock;
struct cnxctx *parent;
/* Add another list to chain in a global list to implement a garbage collector on sessions -- TODO if needed */
};
/* Saved master session data for resuming sessions */
struct sr_data {
struct fd_list chain;
gnutls_datum_t key;
gnutls_datum_t data;
};
/* Initialize the store area for a connection */
static int store_init(struct cnxctx * conn)
{
TRACE_ENTRY("%p", conn);
CHECK_PARAMS( conn && !conn->cc_sctp3436_data.sess_store );
CHECK_MALLOC( conn->cc_sctp3436_data.sess_store = malloc(sizeof(struct sr_store)) );
memset(conn->cc_sctp3436_data.sess_store, 0, sizeof(struct sr_store));
fd_list_init(&conn->cc_sctp3436_data.sess_store->list, NULL);
CHECK_POSIX( pthread_rwlock_init(&conn->cc_sctp3436_data.sess_store->lock, NULL) );
conn->cc_sctp3436_data.sess_store->parent = conn;
return 0;
}
/* Destroy the store area for a connection, and all its content */
static void store_destroy(struct cnxctx * conn)
{
/* Del all list entries */
TRACE_ENTRY("%p", conn);
CHECK_PARAMS_DO( conn, return );
if (!conn->cc_sctp3436_data.sess_store)
return;
CHECK_POSIX_DO( pthread_rwlock_destroy(&conn->cc_sctp3436_data.sess_store->lock), /* continue */ );
while (!FD_IS_LIST_EMPTY(&conn->cc_sctp3436_data.sess_store->list)) {
struct sr_data * sr = (struct sr_data *) conn->cc_sctp3436_data.sess_store->list.next;
fd_list_unlink( &sr->chain );
free(sr->key.data);
free(sr->data.data);
free(sr);
}
free(conn->cc_sctp3436_data.sess_store);
conn->cc_sctp3436_data.sess_store = NULL;
return;
}
/* Search the position (or next if not found) of a key in a store */
static struct fd_list * find_or_next(struct sr_store * sto, gnutls_datum_t key, int * match)
{
struct fd_list * ret;
*match = 0;
for (ret = sto->list.next; ret != &sto->list; ret = ret->next) {
int cmp = 0;
struct sr_data * sr = (struct sr_data *)ret;
cmp = fd_os_cmp(key.data, key.size, sr->key.data, sr->key.size);
if (cmp > 0)
continue;
if (cmp == 0)
*match = 1;
break;
}
return ret;
}
/* Callbacks for the TLS server side of the connection, called during gnutls_handshake */
static int sr_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data)
{
struct sr_store * sto = (struct sr_store *)dbf;
struct fd_list * li;
struct sr_data * sr;
int match = 0;
int ret = 0;
TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
CHECK_PARAMS_DO( sto && key.data && data.data, return -1 );
CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session store [key ", key.data, key.size, "]");
li = find_or_next(sto, key, &match);
if (match) {
sr = (struct sr_data *)li;
/* Check the data is the same */
if ((data.size != sr->data.size) || memcmp(data.data, sr->data.data, data.size)) {
TRACE_DEBUG(INFO, "GnuTLS tried to store a session with same key and different data!");
TRACE_BUFFER(FD_LOG_DEBUG, INFO, "Session store [key ", key.data, key.size, "]");
TRACE_BUFFER(FD_LOG_DEBUG, INFO, " -- old data [", sr->data.data, sr->data.size, "]");
TRACE_BUFFER(FD_LOG_DEBUG, INFO, " -- new data [", data.data, data.size, "]");
ret = -1;
} else {
TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GnuTLS tried to store a session with same key and same data, skipped.");
}
goto out;
}
/* Create a new entry */
CHECK_MALLOC_DO( sr = malloc(sizeof(struct sr_data)), { ret = -1; goto out; } );
memset(sr, 0, sizeof(struct sr_data));
fd_list_init(&sr->chain, sr);
CHECK_MALLOC_DO( sr->key.data = malloc(key.size), { ret = -1; goto out; } );
sr->key.size = key.size;
memcpy(sr->key.data, key.data, key.size);
CHECK_MALLOC_DO( sr->data.data = malloc(data.size), { ret = -1; goto out; } );
sr->data.size = data.size;
memcpy(sr->data.data, data.data, data.size);
/* Save this new entry in the list, we are done */
fd_list_insert_before(li, &sr->chain);
out:
CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
return ret;
}
static int sr_remove (void *dbf, gnutls_datum_t key)
{
struct sr_store * sto = (struct sr_store *)dbf;
struct fd_list * li;
struct sr_data * sr;
int match = 0;
int ret = 0;
TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
CHECK_PARAMS_DO( sto && key.data, return -1 );
CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session delete [key ", key.data, key.size, "]");
li = find_or_next(sto, key, &match);
if (match) {
sr = (struct sr_data *)li;
/* Destroy this data */
fd_list_unlink(li);
free(sr->key.data);
free(sr->data.data);
free(sr);
} else {
/* It was not found */
ret = -1;
}
CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
return ret;
}
static gnutls_datum_t sr_fetch (void *dbf, gnutls_datum_t key)
{
struct sr_store * sto = (struct sr_store *)dbf;
struct fd_list * li;
struct sr_data * sr;
int match = 0;
gnutls_datum_t res = { NULL, 0 };
gnutls_datum_t error = { NULL, 0 };
TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
CHECK_PARAMS_DO( sto && key.data, return error );
CHECK_POSIX_DO( pthread_rwlock_rdlock(&sto->lock), return error );
TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session fetch [key ", key.data, key.size, "]");
li = find_or_next(sto, key, &match);
if (match) {
sr = (struct sr_data *)li;
GNUTLS_TRACE( CHECK_MALLOC_DO(res.data = gnutls_malloc(sr->data.size), goto out ) );
res.size = sr->data.size;
memcpy(res.data, sr->data.data, res.size);
}
out:
TRACE_DEBUG(GNUTLS_DBG_LEVEL, "Fetched (%p, %d) from store %p", res.data, res.size, sto);
CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return error);
return res;
}
/* Set the session pointer in a session object */
static void set_resume_callbacks(gnutls_session_t session, struct cnxctx * conn)
{
TRACE_ENTRY("%p", conn);
GNUTLS_TRACE( gnutls_db_set_retrieve_function(session, sr_fetch));
GNUTLS_TRACE( gnutls_db_set_remove_function (session, sr_remove));
GNUTLS_TRACE( gnutls_db_set_store_function (session, sr_store));
GNUTLS_TRACE( gnutls_db_set_ptr (session, conn->cc_sctp3436_data.sess_store));
return;
}
/* The handshake is made in parallel in several threads to speed up */
static void * handshake_resume_th(void * arg)
{
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) arg;
int resumed;
TRACE_ENTRY("%p", arg);
/* Set the thread name */
{
char buf[48];
snprintf(buf, sizeof(buf), "Handshake resume (%hu@%d)", ctx->strid, ctx->parent->cc_socket);
fd_log_threadname ( buf );
}
TRACE_DEBUG(FULL, "Starting TLS resumed handshake on stream %hu", ctx->strid);
CHECK_GNUTLS_DO( gnutls_handshake( ctx->session ), return NULL);
GNUTLS_TRACE( resumed = gnutls_session_is_resumed(ctx->session) );
#ifndef GNUTLS_VERSION_300
if (!resumed) {
/* Check the credentials here also */
CHECK_FCT_DO( fd_tls_verify_credentials(ctx->session, ctx->parent, 0), return NULL );
}
#endif /* GNUTLS_VERSION_300 */
if (TRACE_BOOL(FULL)) {
if (resumed) {
fd_log_debug("Session was resumed successfully on stream %hu (conn: '%s')", ctx->strid, fd_cnx_getid(ctx->parent));
} else {
fd_log_debug("Session was NOT resumed on stream %hu (full handshake) (conn: '%s')", ctx->strid, fd_cnx_getid(ctx->parent));
}
}
/* Finished, OK */
return arg;
}
/*************************************************************/
/* Exported functions */
/*************************************************************/
/* Initialize the wrapper for the connection */
int fd_sctp3436_init(struct cnxctx * conn)
{
uint16_t i;
TRACE_ENTRY("%p", conn);
CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && (!conn->cc_sctp3436_data.array) );
/* First, alloc the array and initialize the non-TLS data */
CHECK_MALLOC( conn->cc_sctp3436_data.array = calloc(conn->cc_sctp_para.pairs, sizeof(struct sctp3436_ctx)) );
for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
conn->cc_sctp3436_data.array[i].parent = conn;
conn->cc_sctp3436_data.array[i].strid = i;
CHECK_FCT( fd_fifo_new(&conn->cc_sctp3436_data.array[i].raw_recv, 10) );
}
/* Set push/pull functions in the master session, using fifo in array[0] */
set_sess_transport(conn->cc_tls_para.session, &conn->cc_sctp3436_data.array[0]);
/* For server side, we also initialize the resuming capabilities */
if (conn->cc_tls_para.mode == GNUTLS_SERVER) {
/* Prepare the store for sessions data */
CHECK_FCT( store_init(conn) );
/* Set the callbacks for resuming in the master session */
set_resume_callbacks(conn->cc_tls_para.session, conn);
}
/* Start the demux thread */
CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, demuxer, conn ) );
return 0;
}
/* Handshake other streams, after full handshake on the master session */
int fd_sctp3436_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds)
{
uint16_t i;
int errors = 0;
gnutls_datum_t master_data;
TRACE_ENTRY("%p %p", conn, priority);
CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && conn->cc_sctp3436_data.array );
/* Server side: we set all the parameters, the resume callback will take care of resuming the session */
/* Client side: we duplicate the parameters of the master session, then set the transport pointer */
/* For client side, retrieve the master session parameters */
if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
CHECK_GNUTLS_DO( gnutls_session_get_data2(conn->cc_tls_para.session, &master_data), return ENOMEM );
/* For debug: */
if (TRACE_BOOL(GNUTLS_DBG_LEVEL)) {
uint8_t id[256];
size_t ids = sizeof(id);
CHECK_GNUTLS_DO( gnutls_session_get_id(conn->cc_tls_para.session, id, &ids), /* continue */ );
TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Master session id: [", id, ids, "]");
}
}
/* Initialize the session objects and start the handshake in a separate thread */
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
/* Set credentials and priority */
CHECK_FCT( fd_tls_prepare(&conn->cc_sctp3436_data.array[i].session, conn->cc_tls_para.mode, 0, priority, alt_creds) );
/* additional initialization for gnutls 3.x */
#ifdef GNUTLS_VERSION_300
/* the verify function has already been set in the global initialization in config.c */
/* fd_tls_verify_credentials_2 uses the connection */
gnutls_session_set_ptr (conn->cc_sctp3436_data.array[i].session, (void *) conn);
if ((conn->cc_tls_para.cn != NULL) && (conn->cc_tls_para.mode == GNUTLS_CLIENT)) {
/* this might allow virtual hosting on the remote peer */
CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_sctp3436_data.array[i].session, GNUTLS_NAME_DNS, conn->cc_tls_para.cn, strlen(conn->cc_tls_para.cn)), /* ignore failure */);
}
#endif /* GNUTLS_VERSION_300 */
#ifdef GNUTLS_VERSION_310
GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_sctp3436_data.array[i].session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
#endif /* GNUTLS_VERSION_310 */
/* For the client, copy data from master session; for the server, set session resuming pointers */
if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
CHECK_GNUTLS_DO( gnutls_session_set_data(conn->cc_sctp3436_data.array[i].session, master_data.data, master_data.size), return ENOMEM );
} else {
set_resume_callbacks(conn->cc_sctp3436_data.array[i].session, conn);
}
/* Set transport parameters */
set_sess_transport(conn->cc_sctp3436_data.array[i].session, &conn->cc_sctp3436_data.array[i]);
/* Start the handshake thread */
CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[i].thr, NULL, handshake_resume_th, &conn->cc_sctp3436_data.array[i] ) );
}
/* We can now release the memory of master session data if any */
if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
GNUTLS_TRACE( gnutls_free(master_data.data) );
}
/* Now wait for all handshakes to finish */
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
void * ret;
CHECK_POSIX( pthread_join(conn->cc_sctp3436_data.array[i].thr, &ret) );
conn->cc_sctp3436_data.array[i].thr = (pthread_t) NULL;
if (ret == NULL) {
errors++; /* Handshake failed on this stream */
}
}
if (errors) {
TRACE_DEBUG(INFO, "Handshake failed on %d/%hd stream pairs", errors, conn->cc_sctp_para.pairs);
fd_cnx_markerror(conn);
return ENOTCONN;
}
return 0;
}
/* Receive messages from others ? all other stream pairs : the master pair */
int fd_sctp3436_startthreads(struct cnxctx * conn, int others)
{
uint16_t i;
TRACE_ENTRY("%p", conn);
CHECK_PARAMS( conn && conn->cc_sctp3436_data.array );
if (others) {
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
/* Start the decipher thread */
CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[i].thr, NULL, decipher, &conn->cc_sctp3436_data.array[i] ) );
}
} else {
CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[0].thr, NULL, decipher, &conn->cc_sctp3436_data.array[0] ) );
}
return 0;
}
/* Initiate a "bye" on all stream pairs */
void fd_sctp3436_bye(struct cnxctx * conn)
{
uint16_t i;
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
/* End all TLS sessions, in series (not as efficient as paralel, but simpler) */
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
if ( ! fd_cnx_teststate(conn, CC_STATUS_ERROR)) {
CHECK_GNUTLS_DO( gnutls_bye(conn->cc_sctp3436_data.array[i].session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
}
}
}
/* After "bye" was sent on all streams, read from sessions until an error is received */
void fd_sctp3436_waitthreadsterm(struct cnxctx * conn)
{
uint16_t i;
TRACE_ENTRY("%p", conn);
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
if (conn->cc_sctp3436_data.array[i].thr != (pthread_t)NULL) {
CHECK_POSIX_DO( pthread_join(conn->cc_sctp3436_data.array[i].thr, NULL), /* continue */ );
conn->cc_sctp3436_data.array[i].thr = (pthread_t)NULL;
}
}
return;
}
/* Free gnutls resources of all sessions */
void fd_sctp3436_gnutls_deinit_others(struct cnxctx * conn)
{
uint16_t i;
TRACE_ENTRY("%p", conn);
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
if (conn->cc_sctp3436_data.array[i].session) {
GNUTLS_TRACE( gnutls_deinit(conn->cc_sctp3436_data.array[i].session) );
conn->cc_sctp3436_data.array[i].session = NULL;
}
}
}
/* Stop all receiver threads */
void fd_sctp3436_stopthreads(struct cnxctx * conn)
{
uint16_t i;
TRACE_ENTRY("%p", conn);
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
CHECK_FCT_DO( fd_thr_term(&conn->cc_sctp3436_data.array[i].thr), /* continue */ );
}
return;
}
/* Destroy a wrapper context */
void fd_sctp3436_destroy(struct cnxctx * conn)
{
uint16_t i;
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
/* Terminate all receiving threads in case we did not do it yet */
fd_sctp3436_stopthreads(conn);
/* Now, stop the demux thread */
CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
/* Free remaining data in the array */
for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
if (conn->cc_sctp3436_data.array[i].raw_recv)
fd_event_destroy( &conn->cc_sctp3436_data.array[i].raw_recv, free );
free(conn->cc_sctp3436_data.array[i].partial.buf);
if (conn->cc_sctp3436_data.array[i].session) {
GNUTLS_TRACE( gnutls_deinit(conn->cc_sctp3436_data.array[i].session) );
conn->cc_sctp3436_data.array[i].session = NULL;
}
}
/* Free the array itself now */
free(conn->cc_sctp3436_data.array);
conn->cc_sctp3436_data.array = NULL;
/* Delete the store of sessions */
store_destroy(conn);
return ;
}
+509
View File
@@ -0,0 +1,509 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
/* Server (listening) part of the framework */
static struct fd_list FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS); /* The list of all server objects */
/* We don't need to protect this list, it is only accessed from the main framework thread. */
enum s_state {
NOT_CREATED=0,
RUNNING,
TERMINATED,
ERROR /* an error occurred, this is not a valid status */
};
/* Servers information */
struct server {
struct fd_list chain; /* link in the FD_SERVERS list */
struct cnxctx * conn; /* server connection context (listening socket) */
int proto; /* IPPROTO_TCP or IPPROTO_SCTP */
int secur; /* TLS is started immediatly after connection ? 0: no; 1: RFU; 2: yes (TLS/TCP or TLS/SCTP) */
pthread_t thr; /* The thread waiting for new connections (will store the data in the clients fifo) */
enum s_state state; /* state of the thread */
struct fifo *pending; /* FIFO of struct cnxctx */
struct pool_workers {
struct server * s; /* pointer to the parent server structure */
int id; /* The worker id for logs */
pthread_t worker; /* The thread */
} *workers; /* array of cnf_thr_srv items */
};
/* Micro functions to read/change the status thread-safely */
static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER;
static enum s_state get_status(struct server * s)
{
enum s_state r;
CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return ERROR );
r = s->state;
CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return ERROR );
return r;
}
static void set_status(struct server * s, enum s_state st)
{
CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return );
s->state = st;
CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return );
}
/* dump one item of the server->pending fifo */
static DECLARE_FD_DUMP_PROTOTYPE(dump_cnx, void * item) {
struct cnxctx * c = item;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " '%s'", fd_cnx_getid(c)), return NULL);
return *buf;
}
/* Dump all servers information */
DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump, int details)
{
struct fd_list * li;
FD_DUMP_HANDLE_OFFSET();
for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
struct server * s = (struct server *)li;
enum s_state st = get_status(s);
if (details) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{server}(@%p)'%s': %s, %s(%d), %s", s, fd_cnx_getid(s->conn),
IPPROTO_NAME( s->proto ),
s->secur ? "Secur" : "NotSecur", s->secur,
(st == NOT_CREATED) ? "Thread not created" :
((st == RUNNING) ? "Thread running" :
((st == TERMINATED) ? "Thread terminated" :
"Thread status unknown"))), return NULL);
/* Dump the client list of this server */
CHECK_MALLOC_DO( fd_fifo_dump(FD_DUMP_STD_PARAMS, "pending connections", s->pending, dump_cnx), return NULL );
if (li->next != &FD_SERVERS) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
}
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(%s,%s) ", fd_cnx_getid(s->conn),
IPPROTO_NAME( s->proto ), s->secur ? "Secur" : "NotSecur"), return NULL);
}
}
return *buf;
}
/* The thread in the pool for handling new clients connecting to a server */
static void * client_worker(void * arg)
{
struct pool_workers * pw = arg;
struct server * s = pw->s;
struct cnxctx * c = NULL;
int fatal = 0;
struct timespec ts;
struct fd_cnx_rcvdata rcv_data;
struct fd_msg_pmdl * pmdl = NULL;
struct msg * msg = NULL;
struct msg_hdr *hdr = NULL;
struct fd_pei pei;
TRACE_ENTRY("%p", arg);
/* Set the thread name */
{
char buf[48];
snprintf(buf, sizeof(buf), "Worker#%d[%s%s]", pw->id, IPPROTO_NAME(s->proto), s->secur?", Sec" : "");
fd_log_threadname ( buf );
}
/* Loop until canceled / error */
next_client:
LOG_A("Ready to process next incoming connection");
memset(&rcv_data, 0, sizeof(rcv_data));
/* Get the next connection */
CHECK_FCT_DO( fd_fifo_get( s->pending, &c ), { fatal = 1; goto cleanup; } );
/* Handshake if we are a secure server port, or start clear otherwise */
if (s->secur) {
LOG_D("Starting handshake with %s", fd_cnx_getid(c));
int ret = fd_cnx_handshake(c, GNUTLS_SERVER, (s->secur == 1) ? ALGO_HANDSHAKE_DEFAULT : ALGO_HANDSHAKE_3436, NULL, NULL);
if (ret != 0) {
char buf[1024];
snprintf(buf, sizeof(buf), "TLS handshake failed for connection '%s', connection closed.", fd_cnx_getid(c));
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
goto cleanup;
}
} else {
CHECK_FCT_DO( fd_cnx_start_clear(c, 0), goto cleanup );
}
/* Set the timeout to receive the first message */
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), { fatal = 1; goto cleanup; } );
ts.tv_sec += INCNX_TIMEOUT;
/* Receive the first Diameter message on the connection -- cleanup in case of timeout */
CHECK_FCT_DO( fd_cnx_receive(c, &ts, &rcv_data.buffer, &rcv_data.length),
{
char buf[1024];
switch (__ret__) {
case ETIMEDOUT:
snprintf(buf, sizeof(buf), "Client '%s' did not send CER within %ds, connection aborted.", fd_cnx_getid(c), INCNX_TIMEOUT);
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
break;
case ENOTCONN:
snprintf(buf, sizeof(buf), "Connection from '%s' in error before CER was received.", fd_cnx_getid(c));
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
break;
default:
snprintf(buf, sizeof(buf), "Connection from '%s': unspecified error, connection aborted.", fd_cnx_getid(c));
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
}
goto cleanup;
} );
TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", rcv_data.length, fd_cnx_getid(c));
pmdl = fd_msg_pmdl_get_inbuf(rcv_data.buffer, rcv_data.length);
/* Try parsing this message */
CHECK_FCT_DO( fd_msg_parse_buffer( &rcv_data.buffer, rcv_data.length, &msg ),
{ /* Parsing failed */
fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, NULL, NULL, &rcv_data, pmdl );
goto cleanup;
} );
/* Log incoming message */
fd_hook_associate(msg, pmdl);
fd_hook_call(HOOK_MESSAGE_RECEIVED, msg, NULL, fd_cnx_getid(c), fd_msg_pmdl_get(msg));
/* We expect a CER, it must parse with our dictionary and rules */
CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ),
{ /* Parsing failed -- trace details */
char buf[1024];
fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msg, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(msg));
snprintf(buf, sizeof(buf), "Error parsing CER from '%s', connection aborted.", fd_cnx_getid(c));
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
goto cleanup;
} );
/* Now check we received a CER */
CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), { fatal = 1; goto cleanup; } );
CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE),
{ /* Parsing failed -- trace details */
char buf[1024];
snprintf(buf, sizeof(buf), "Expected CER from '%s', received a different message, connection aborted.", fd_cnx_getid(c));
fd_hook_call(HOOK_PEER_CONNECT_FAILED, msg, NULL, buf, NULL);
goto cleanup;
} );
/* Finally, pass the information to the peers module which will handle it in a separate thread */
pthread_cleanup_push((void *)fd_cnx_destroy, c);
pthread_cleanup_push((void *)fd_msg_free, msg);
CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c ), );
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
cleanup:
/* Cleanup the parsed message if any */
if (msg) {
CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
msg = NULL;
}
/* Close the connection if needed */
if (c != NULL) {
fd_cnx_destroy(c);
c = NULL;
}
/* Cleanup the received buffer if any */
free(rcv_data.buffer);
if (!fatal)
goto next_client;
LOG_E("Worker thread exiting.");
return NULL;
}
/* The thread managing a server */
static void * serv_th(void * arg)
{
struct server *s = (struct server *)arg;
CHECK_PARAMS_DO(s, goto error);
fd_log_threadname ( fd_cnx_getid(s->conn) );
set_status(s, RUNNING);
/* Accept incoming connections */
CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
do {
struct cnxctx * conn = NULL;
/* Wait for a new client or cancel */
CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), break );
/* Store this connection in the fifo for processing by the worker pool. Will block when the fifo is full */
pthread_cleanup_push((void *)fd_cnx_destroy, conn);
CHECK_FCT_DO( fd_fifo_post( s->pending, &conn ), break );
pthread_cleanup_pop(0);
} while (1);
error:
if (s)
set_status(s, TERMINATED);
/* Send error signal to the core */
LOG_F( "An error occurred in server module! Thread is terminating...");
CHECK_FCT_DO(fd_core_shutdown(), );
return NULL;
}
/* Create a new server structure */
static struct server * new_serv( int proto, int secur )
{
struct server * new;
int i;
/* New server structure */
CHECK_MALLOC_DO( new = malloc(sizeof(struct server)), return NULL );
memset(new, 0, sizeof(struct server));
fd_list_init(&new->chain, new);
new->proto = proto;
new->secur = secur;
CHECK_FCT_DO( fd_fifo_new(&new->pending, 5), return NULL);
CHECK_MALLOC_DO( new->workers = calloc( fd_g_config->cnf_thr_srv, sizeof(struct pool_workers) ), return NULL );
for (i = 0; i < fd_g_config->cnf_thr_srv; i++) {
/* Create the pool */
new->workers[i].s = new;
new->workers[i].id = i;
CHECK_POSIX_DO( pthread_create( &new->workers[i].worker, NULL, client_worker, &new->workers[i]), return NULL );
}
return new;
}
/* Start all the servers */
int fd_servers_start()
{
struct server * s;
int empty_conf_ep = FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints);
/* SCTP */
if (!fd_g_config->cnf_flags.no_sctp) {
#ifdef DISABLE_SCTP
ASSERT(0);
#else /* DISABLE_SCTP */
/* Create the server on unsecure port */
if (fd_g_config->cnf_port) {
CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}
/* Create the server on secure port */
if (fd_g_config->cnf_port_tls) {
CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 2 /* Change when DTLS is introduced */) );
CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}
/* Create the other server on 3436 secure port */
/*if (fd_g_config->cnf_port_3436) {
CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 2) );
CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_3436, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}*/
#endif /* DISABLE_SCTP */
}
/* TCP */
if (!fd_g_config->cnf_flags.no_tcp) {
if (empty_conf_ep) {
/* Bind TCP servers on [0.0.0.0] */
if (!fd_g_config->cnf_flags.no_ip4) {
if (fd_g_config->cnf_port) {
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}
if (fd_g_config->cnf_port_tls) {
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}
}
/* Bind TCP servers on [::] */
if (!fd_g_config->cnf_flags.no_ip6) {
if (fd_g_config->cnf_port) {
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}
if (fd_g_config->cnf_port_tls) {
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}
}
} else {
/* Create all endpoints -- check flags */
struct fd_list * li;
for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
struct fd_endpoint * ep = (struct fd_endpoint *)li;
sSA * sa = (sSA *) &ep->ss;
if (! (ep->flags & EP_FL_CONF))
continue;
if (fd_g_config->cnf_flags.no_ip4 && (sa->sa_family == AF_INET))
continue;
if (fd_g_config->cnf_flags.no_ip6 && (sa->sa_family == AF_INET6))
continue;
if (fd_g_config->cnf_port) {
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}
if (fd_g_config->cnf_port_tls) {
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) );
fd_list_insert_before( &FD_SERVERS, &s->chain );
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
}
}
}
}
/* Now, if we had an empty list of local adresses (no address configured), try to read the real addresses from the kernel */
if (empty_conf_ep) {
CHECK_FCT(fd_cnx_get_local_eps(&fd_g_config->cnf_endpoints));
if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
TRACE_DEBUG(INFO, "Unable to find the address(es) of the local system. "
"Please use \"ListenOn\" parameter in the configuration. "
"This information is required to generate the CER/CEA messages.");
return EINVAL;
}
}
{
char * buf = NULL;
size_t len = 0, offset = 0;
CHECK_MALLOC_DO( fd_dump_extend( &buf, &len, &offset , "Local server address(es): "), );
CHECK_MALLOC_DO( fd_ep_dump( &buf, &len, &offset, 0, 0, &fd_g_config->cnf_endpoints ), );
LOG_N("%s", buf ?: "Error dumping addresses");
free(buf);
}
return 0;
}
/* Terminate all the servers */
int fd_servers_stop()
{
TRACE_ENTRY("");
TRACE_DEBUG(INFO, "Shutting down server sockets...");
/* Loop on all servers */
while (!FD_IS_LIST_EMPTY(&FD_SERVERS)) {
struct server * s = (struct server *)(FD_SERVERS.next);
int i;
struct cnxctx * c;
/* cancel thread */
CHECK_FCT_DO( fd_thr_term(&s->thr), /* continue */);
/* destroy server connection context */
fd_cnx_destroy(s->conn);
/* cancel and destroy all worker threads */
for (i = 0; i < fd_g_config->cnf_thr_srv; i++) {
/* Destroy worker thread */
CHECK_FCT_DO( fd_thr_term(&s->workers[i].worker), /* continue */);
}
free(s->workers);
/* Close any pending connection */
while ( fd_fifo_tryget( s->pending, &c ) == 0 ) {
fd_cnx_destroy(c);
}
CHECK_FCT_DO( fd_fifo_del(&s->pending), );
/* Now destroy the server object */
fd_list_unlink(&s->chain);
free(s);
}
/* We're done! */
return 0;
}
+168
View File
@@ -0,0 +1,168 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 "fdcore-internal.h"
#include "cnxctx.h"
#include <netinet/tcp.h>
#include <netinet/ip6.h>
#include <sys/socket.h>
/* Set the socket options for TCP sockets, before bind is called */
static int fd_tcp_setsockopt(int family, int sk)
{
int ret = 0;
int opt;
/* Clear the NODELAY option in case it was set, as requested by rfc3539#section-3.2 */
/* Note that this is supposed to be the default, so we could probably remove this call ... */
opt = 0;
ret = setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
if (ret != 0) {
ret = errno;
TRACE_DEBUG(INFO, "Unable to set the socket TCP_NODELAY option: %s", strerror(ret));
return ret;
}
/* Under Linux, we may also set the TCP_CONGESTION option to one of the following strings:
- reno (default)
- bic
- cubic
- highspeed
- htcp
- hybla
- illinois
- lp
- scalable
- vegas
- veno
- westwood
- yeah
*/
/* In case of v6 address, force the v6only option, we use a different socket for v4 */
#ifdef IPV6_V6ONLY
if (family == AF_INET6) {
opt = 1;
CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
}
#endif /* IPV6_V6ONLY */
{
opt = 1;
CHECK_SYS( setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) );
}
/* There are also others sockopt that can be set, but nothing useful for us AFAICT */
return 0;
}
/* Create a socket server and bind it */
int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen )
{
TRACE_ENTRY("%p %p %d", sock, sa, salen);
CHECK_PARAMS( sock && sa );
/* Create the socket */
CHECK_SYS( *sock = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP) );
/* Set the socket options */
CHECK_FCT( fd_tcp_setsockopt(sa->sa_family, *sock) );
/* Bind the socket */
CHECK_SYS( bind( *sock, sa, salen ) );
/* We're done */
return 0;
}
/* Allow clients connections on server sockets */
int fd_tcp_listen( int sock )
{
TRACE_ENTRY("%d", sock);
CHECK_SYS( listen(sock, 5) );
return 0;
}
/* Create a client socket and connect to remote server */
int fd_tcp_client( int *sock, sSA * sa, socklen_t salen )
{
int ret = 0;
int s;
TRACE_ENTRY("%p %p %d", sock, sa, salen);
CHECK_PARAMS( sock && (*sock <= 0) && sa && salen );
/* Create the socket */
CHECK_SYS( s = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP) );
/* Set the socket options */
CHECK_FCT( fd_tcp_setsockopt(sa->sa_family, s) );
/* Cleanup if we are cancelled */
pthread_cleanup_push(fd_cleanup_socket, &s);
/* Try connecting to the remote address */
ret = connect(s, sa, salen);
pthread_cleanup_pop(0);
if (ret < 0) {
ret = errno;
LOG_A( "connect returned an error: %s", strerror(ret));
CHECK_SYS_DO( close(s), /* continue */ );
*sock = -1;
return ret;
}
/* Done! */
*sock = s;
return ret;
}
/* Get the remote name of a TCP socket */
int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl)
{
TRACE_ENTRY("%d %p %p", sock, ss, sl);
CHECK_PARAMS( ss && sl );
*sl = sizeof(sSS);
CHECK_SYS(getpeername(sock, (sSA *)ss, sl));
return 0;
}
+47
View File
@@ -0,0 +1,47 @@
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, 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 <fdcore-internal.h>
#include "version.h"
#ifdef FD_PROJECT_VERSION_HG
# define FD_LIBFDCORE_VERSION \
_stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV) "-" FD_PROJECT_VERSION_HG_VAL
#else
# define FD_LIBFDCORE_VERSION \
_stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV)
#endif
const char fd_core_version[] = FD_LIBFDCORE_VERSION;
+3
View File
@@ -0,0 +1,3 @@
/* Following variable is expended at build time based on the result of "hg id" command */
#define FD_PROJECT_VERSION_HG
#define FD_PROJECT_VERSION_HG_VAL "1304(5a505af0bba0)"