diff options
Diffstat (limited to 'libfdcore')
| -rw-r--r-- | libfdcore/cnxctx.c | 430 | ||||
| -rw-r--r-- | libfdcore/cnxctx.h | 12 | ||||
| -rw-r--r-- | libfdcore/config.c | 19 | ||||
| -rw-r--r-- | libfdcore/core.c | 4 | ||||
| -rw-r--r-- | libfdcore/dict_base_proto.c | 88 | ||||
| -rw-r--r-- | libfdcore/fdcore-internal.h | 5 | ||||
| -rw-r--r-- | libfdcore/fdd.l | 68 | ||||
| -rw-r--r-- | libfdcore/fdd.y | 95 | ||||
| -rw-r--r-- | libfdcore/hooks.c | 4 | ||||
| -rw-r--r-- | libfdcore/messages.c | 261 | ||||
| -rw-r--r-- | libfdcore/p_ce.c | 44 | ||||
| -rw-r--r-- | libfdcore/p_cnx.c | 6 | ||||
| -rw-r--r-- | libfdcore/p_out.c | 2 | ||||
| -rw-r--r-- | libfdcore/p_psm.c | 268 | ||||
| -rw-r--r-- | libfdcore/peers.c | 4 | ||||
| -rw-r--r-- | libfdcore/queues.c | 18 | ||||
| -rw-r--r-- | libfdcore/routing_dispatch.c | 65 | ||||
| -rw-r--r-- | libfdcore/sctp.c | 164 | ||||
| -rw-r--r-- | libfdcore/sctp3436.c | 4 |
19 files changed, 950 insertions, 611 deletions
diff --git a/libfdcore/cnxctx.c b/libfdcore/cnxctx.c index 04934e0..dab6812 100644 --- a/libfdcore/cnxctx.c +++ b/libfdcore/cnxctx.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -176,7 +176,7 @@ struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list) } else { cnx->cc_family = AF_INET6; /* can create socket for both IP and IPv6 */ } - + /* Create the socket */ CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, cnx->cc_family, ep_list, port ), goto error ); @@ -226,41 +226,41 @@ struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv) TRACE_ENTRY("%p", serv); CHECK_PARAMS_DO(serv, return NULL); - + /* Accept the new connection -- this is blocking until new client enters or until cancellation */ CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL ); - + CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); close(cli_sock); return NULL; } ); cli->cc_socket = cli_sock; cli->cc_family = serv->cc_family; cli->cc_proto = serv->cc_proto; - + /* Set the timeout */ fd_cnx_s_setto(cli->cc_socket); - + /* Generate the name for the connection object */ { char addrbuf[INET6_ADDRSTRLEN]; char portbuf[10]; int rc; - + rc = getnameinfo((sSA *)&ss, ss_len, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (rc) { snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); portbuf[0] = '\0'; } - + /* Numeric values for debug... */ - snprintf(cli->cc_id, sizeof(cli->cc_id), CC_ID_HDR "%s from [%s]:%s (%d<-%d)", + snprintf(cli->cc_id, sizeof(cli->cc_id), CC_ID_HDR "%s from [%s]:%s (%d<-%d)", IPPROTO_NAME(cli->cc_proto), addrbuf, portbuf, serv->cc_socket, cli->cc_socket); - - + + /* ...Name for log messages */ rc = getnameinfo((sSA *)&ss, ss_len, cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0); if (rc) snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc)); } - + LOG_D("Incoming connection: '%s' <- '%s' {%s}", fd_cnx_getid(serv), cli->cc_remid, cli->cc_id); #ifndef DISABLE_SCTP @@ -272,7 +272,7 @@ struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv) cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_out; else cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_in; - + LOG_A( "%s : client '%s' (SCTP:%d, %d/%d streams)", fd_cnx_getid(serv), fd_cnx_getid(cli), cli->cc_socket, cli->cc_sctp_para.str_in, cli->cc_sctp_para.str_out); } #endif /* DISABLE_SCTP */ @@ -286,14 +286,14 @@ struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, int sock = 0; struct cnxctx * cnx = NULL; char sa_buf[sSA_DUMP_STRLEN]; - + TRACE_ENTRY("%p %d", sa, addrlen); CHECK_PARAMS_DO( sa && addrlen, return NULL ); - + fd_sa_sdump_numeric(sa_buf, sa); - + LOG_D("Connecting to TCP %s...", sa_buf); - + /* Create the socket and connect, which can take some time and/or fail */ { int ret = fd_tcp_client( &sock, sa, addrlen ); @@ -302,36 +302,37 @@ struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, return NULL; } } - + /* Once the socket is created successfuly, prepare the remaining of the cnx */ CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } ); - + cnx->cc_socket = sock; cnx->cc_family = sa->sa_family; cnx->cc_proto = IPPROTO_TCP; - + /* Set the timeout */ fd_cnx_s_setto(cnx->cc_socket); - + /* Generate the names for the object */ { int rc; - + snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "TCP,#%d->%s", cnx->cc_socket, sa_buf); - + /* ...Name for log messages */ rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); if (rc) snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); } - + LOG_A("TCP connection to %s succeed (socket:%d).", sa_buf, sock); - + return cnx; } -/* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used) */ -struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list) +/* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used). + * If src_list is not NULL and not empty, list of local addresses to connect from via sctp_bindx(). */ +struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list, struct fd_list * src_list) { #ifdef DISABLE_SCTP TRACE_DEBUG(INFO, "This function should never be called when SCTP is disabled..."); @@ -343,55 +344,72 @@ struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_lis struct cnxctx * cnx = NULL; char sa_buf[sSA_DUMP_STRLEN]; sSS primary; - - TRACE_ENTRY("%p", list); + + TRACE_ENTRY("%p %p", list, src_list); CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL ); - + + /* Log SCTP association source and destination endpoints */ + { + char * buf = NULL; + size_t len = 0, offset = 0; + CHECK_MALLOC_DO( fd_dump_extend( &buf, &len, &offset, "Connecting SCTP endpoints"), ); + CHECK_MALLOC_DO( fd_dump_extend( &buf, &len, &offset, " source: "), ); + if (src_list && !FD_IS_LIST_EMPTY(src_list)) { + CHECK_MALLOC_DO( fd_ep_dump( &buf, &len, &offset, 0, 0, src_list ), ); + } else { + CHECK_MALLOC_DO( fd_dump_extend( &buf, &len, &offset, "(ANY)"), ); + } + CHECK_MALLOC_DO( fd_dump_extend( &buf, &len, &offset, ", destination: "), ); + CHECK_MALLOC_DO( fd_ep_dump( &buf, &len, &offset, 0, 0, list ), ); + LOG_D("%s", buf ?: "Error determining SCTP endpoints"); + free(buf); + } + fd_sa_sdump_numeric(sa_buf, &((struct fd_endpoint *)(list->next))->sa); - + LOG_D("Connecting to SCTP %s:%hu...", sa_buf, port); - + { - int ret = fd_sctp_client( &sock, no_ip6, port, list ); + int ret = fd_sctp_client( &sock, no_ip6, port, list, src_list ); if (ret != 0) { LOG_D("SCTP connection to [%s,...] failed: %s", sa_buf, strerror(ret)); return NULL; } } - + /* Once the socket is created successfuly, prepare the remaining of the cnx */ CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } ); - + cnx->cc_socket = sock; cnx->cc_family = no_ip6 ? AF_INET : AF_INET6; cnx->cc_proto = IPPROTO_SCTP; - + /* Set the timeout */ fd_cnx_s_setto(cnx->cc_socket); - + /* Retrieve the number of streams and primary address */ CHECK_FCT_DO( fd_sctp_get_str_info( sock, &cnx->cc_sctp_para.str_in, &cnx->cc_sctp_para.str_out, &primary ), goto error ); if (cnx->cc_sctp_para.str_out < cnx->cc_sctp_para.str_in) cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_out; else cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_in; - + fd_sa_sdump_numeric(sa_buf, (sSA *)&primary); - + /* Generate the names for the object */ { int rc; - + snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "SCTP,#%d->%s", cnx->cc_socket, sa_buf); - + /* ...Name for log messages */ rc = getnameinfo((sSA *)&primary, sSAlen(&primary), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); if (rc) snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); } - + LOG_A("SCTP connection to %s succeed (socket:%d, %d/%d streams).", sa_buf, sock, cnx->cc_sctp_para.str_in, cnx->cc_sctp_para.str_out); - + return cnx; error: @@ -444,17 +462,17 @@ void fd_cnx_update_id(struct cnxctx * conn) { conn->cc_id[1] = 'C'; else conn->cc_id[1] = '-'; - + if (conn->cc_state & CC_STATUS_ERROR) conn->cc_id[2] = 'E'; else conn->cc_id[2] = '-'; - + if (conn->cc_state & CC_STATUS_SIGNALED) conn->cc_id[3] = 'S'; else conn->cc_id[3] = '-'; - + if (conn->cc_state & CC_STATUS_TLS) conn->cc_id[4] = 'T'; else @@ -508,7 +526,7 @@ int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps) { TRACE_ENTRY("%p %p", conn, eps); CHECK_PARAMS(conn && eps); - + /* Check we have a full connection object, not a listening socket (with no remote) */ CHECK_PARAMS( conn->cc_incoming ); @@ -546,16 +564,16 @@ char * fd_cnx_getremoteid(struct cnxctx * conn) static int fd_cnx_may_dtls(struct cnxctx * conn); /* Get a short string representing the connection */ -int fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len) +int fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len) { CHECK_PARAMS( conn ); - + if (fd_cnx_teststate(conn, CC_STATUS_TLS)) { snprintf(buf, len, "%s,%s,soc#%d", IPPROTO_NAME(conn->cc_proto), fd_cnx_may_dtls(conn) ? "DTLS" : "TLS", conn->cc_socket); } else { snprintf(buf, len, "%s,soc#%d", IPPROTO_NAME(conn->cc_proto), conn->cc_socket); } - + return 0; } @@ -563,27 +581,27 @@ int fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len) int fd_cnx_get_local_eps(struct fd_list * list) { struct ifaddrs *iflist, *cur; - + CHECK_SYS(getifaddrs(&iflist)); - + for (cur = iflist; cur != NULL; cur = cur->ifa_next) { if (cur->ifa_flags & IFF_LOOPBACK) continue; - + if (cur->ifa_addr == NULL) /* may happen with ppp interfaces */ continue; - + if (fd_g_config->cnf_flags.no_ip4 && (cur->ifa_addr->sa_family == AF_INET)) continue; - + if (fd_g_config->cnf_flags.no_ip6 && (cur->ifa_addr->sa_family == AF_INET6)) continue; - + CHECK_FCT(fd_ep_add_merge( list, cur->ifa_addr, sSAlen(cur->ifa_addr), EP_FL_LL )); } - + freeifaddrs(iflist); - + return 0; } @@ -597,31 +615,31 @@ void fd_cnx_markerror(struct cnxctx * conn) { TRACE_ENTRY("%p", conn); CHECK_PARAMS_DO( conn, goto fatal ); - + TRACE_DEBUG(FULL, "Error flag set for socket %d (%s, %s)", conn->cc_socket, conn->cc_id, conn->cc_remid); /* Mark the error */ fd_cnx_addstate(conn, CC_STATUS_ERROR); - + /* Report the error if not reported yet, and not closing */ if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING | CC_STATUS_SIGNALED )) { TRACE_DEBUG(FULL, "Sending FDEVP_CNX_ERROR event"); CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal); fd_cnx_addstate(conn, CC_STATUS_SIGNALED); } - + return; fatal: /* An unrecoverable error occurred, stop the daemon */ ASSERT(0); - CHECK_FCT_DO(fd_core_shutdown(), ); + CHECK_FCT_DO(fd_core_shutdown(), ); } /* Set the timeout option on the socket */ -void fd_cnx_s_setto(int sock) +void fd_cnx_s_setto(int sock) { struct timeval tv; - + /* Set a timeout on the socket so that in any case we are not stuck waiting for something */ memset(&tv, 0, sizeof(tv)); tv.tv_usec = 100000L; /* 100ms, to react quickly to head-of-the-line blocking. */ @@ -636,15 +654,15 @@ static int fd_cnx_s_select (struct cnxctx * conn, unsigned int ms) { fd_set rfds; struct timeval tv; - + FD_ZERO (&rfds); FD_SET (conn->cc_socket, &rfds); - + tv.tv_sec = ms / 1000; tv.tv_usec = (ms * 1000) % 1000000; - + return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv); -} +} #endif /* GNUTLS_VERSION_300 */ /* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */ @@ -664,13 +682,13 @@ again: goto again; } } - + /* Mark the error */ if (ret <= 0) { CHECK_SYS_DO(ret, /* continue, this is only used to log the error here */); fd_cnx_markerror(conn); } - + return ret; } @@ -686,7 +704,7 @@ again: if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) { ret = -errno; pthread_testcancel(); - + /* Check how much time we were blocked for this sending. */ CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), return -1 ); if ( ((now.tv_sec - ts.tv_sec) * 1000 + ((now.tv_nsec - ts.tv_nsec) / 1000000L)) > MAX_HOTL_BLOCKING_TIME) { @@ -694,17 +712,17 @@ again: } else if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) { goto again; /* don't care, just ignore */ } - + /* propagate the error */ errno = -ret; ret = -1; CHECK_SYS_DO(ret, /* continue */); } - + /* Mark the error */ if (ret <= 0) fd_cnx_markerror(conn); - + return ret; } @@ -730,7 +748,7 @@ size_t fd_msg_pmdl_sizewithoverhead(size_t datalen) struct fd_msg_pmdl * fd_msg_pmdl_get_inbuf(uint8_t * buf, size_t datalen) { return (struct fd_msg_pmdl *)(buf + PMDL_PADDED(datalen)); -} +} static int fd_cnx_init_msg_buffer(uint8_t * buffer, size_t expected_len, struct fd_msg_pmdl ** pmdl) { @@ -743,7 +761,7 @@ static int fd_cnx_init_msg_buffer(uint8_t * buffer, size_t expected_len, struct static uint8_t * fd_cnx_alloc_msg_buffer(size_t expected_len, struct fd_msg_pmdl ** pmdl) { uint8_t * ret = NULL; - + CHECK_MALLOC_DO( ret = malloc( fd_msg_pmdl_sizewithoverhead(expected_len) ), return NULL ); CHECK_FCT_DO( fd_cnx_init_msg_buffer(ret, expected_len, pmdl), {free(ret); return NULL;} ); return ret; @@ -753,14 +771,14 @@ static uint8_t * fd_cnx_alloc_msg_buffer(size_t expected_len, struct fd_msg_pmdl static uint8_t * fd_cnx_realloc_msg_buffer(uint8_t * buffer, size_t expected_len, struct fd_msg_pmdl ** pmdl) { uint8_t * ret = NULL; - + CHECK_MALLOC_DO( ret = realloc( buffer, fd_msg_pmdl_sizewithoverhead(expected_len) ), return NULL ); CHECK_FCT_DO( fd_cnx_init_msg_buffer(ret, expected_len, pmdl), {free(ret); return NULL;} ); return ret; } #endif /* DISABLE_SCTP */ -static void free_rcvdata(void * arg) +static void free_rcvdata(void * arg) { struct fd_cnx_rcvdata * data = arg; struct fd_msg_pmdl * pmdl = fd_msg_pmdl_get_inbuf(data->buffer, data->length); @@ -772,21 +790,21 @@ static void free_rcvdata(void * arg) static void * rcvthr_notls_tcp(void * arg) { struct cnxctx * conn = arg; - + 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), "Receiver (%d) TCP/noTLS)", conn->cc_socket); fd_log_threadname ( buf ); } - + ASSERT( conn->cc_proto == IPPROTO_TCP ); ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) ); ASSERT( fd_cnx_target_queue(conn) ); - + /* Receive from a TCP connection: we have to rebuild the message boundaries */ do { uint8_t header[4]; @@ -802,7 +820,7 @@ static void * rcvthr_notls_tcp(void * arg) } received += ret; - + if (header[0] != DIAMETER_VERSION) break; /* No need to wait for 4 bytes in this case */ } while (received < sizeof(header)); @@ -833,22 +851,22 @@ static void * rcvthr_notls_tcp(void * arg) } received += ret; } - + fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl); - + /* We have received a complete message, pass it to the daemon */ - CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), - { + CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), + { free_rcvdata(&rcv_data); - goto fatal; + goto fatal; } ); - + } while (conn->cc_loop); - + out: - TRACE_DEBUG(FULL, "Thread terminated"); + TRACE_DEBUG(FULL, "Thread terminated"); return NULL; - + fatal: /* An unrecoverable error occurred, stop the daemon */ CHECK_FCT_DO(fd_core_shutdown(), ); @@ -862,21 +880,21 @@ static void * rcvthr_notls_sctp(void * arg) struct cnxctx * conn = arg; struct fd_cnx_rcvdata rcv_data; int event; - + TRACE_ENTRY("%p", arg); CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto fatal); - + /* Set the thread name */ { char buf[48]; snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket); fd_log_threadname ( buf ); } - + ASSERT( conn->cc_proto == IPPROTO_SCTP ); ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) ); ASSERT( fd_cnx_target_queue(conn) ); - + do { struct fd_msg_pmdl *pmdl=NULL; CHECK_FCT_DO( fd_sctp_recvmeta(conn, NULL, &rcv_data.buffer, &rcv_data.length, &event), goto fatal ); @@ -884,7 +902,7 @@ static void * rcvthr_notls_sctp(void * arg) fd_cnx_markerror(conn); goto out; } - + if (event == FDEVP_CNX_SHUTDOWN) { /* Just ignore the notification for now, we will get another error later anyway */ continue; @@ -895,11 +913,11 @@ static void * rcvthr_notls_sctp(void * arg) fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl); } CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, rcv_data.length, rcv_data.buffer), goto fatal ); - + } while (conn->cc_loop || (event != FDEVP_CNX_MSG_RECV)); - + out: - TRACE_DEBUG(FULL, "Thread terminated"); + TRACE_DEBUG(FULL, "Thread terminated"); return NULL; fatal: @@ -913,15 +931,15 @@ fatal: int fd_cnx_start_clear(struct cnxctx * conn, int loop) { TRACE_ENTRY("%p %i", conn, loop); - + CHECK_PARAMS( conn && fd_cnx_target_queue(conn) && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && (!conn->cc_loop)); - + /* Release resources in case of a previous call was already made */ CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); - + /* Save the loop request */ conn->cc_loop = loop; - + switch (conn->cc_proto) { case IPPROTO_TCP: /* Start the tcp_notls thread */ @@ -938,7 +956,7 @@ int fd_cnx_start_clear(struct cnxctx * conn, int loop) ASSERT(0); return ENOTSUP; } - + return 0; } @@ -949,11 +967,11 @@ int fd_cnx_start_clear(struct cnxctx * conn, int loop) static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) { ssize_t ret; -again: - CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), +again: + CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), { switch (ret) { - case GNUTLS_E_REHANDSHAKE: + case GNUTLS_E_REHANDSHAKE: if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) { CHECK_GNUTLS_DO( ret = gnutls_handshake(session), { @@ -975,7 +993,7 @@ again: /* The connection is closed */ TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed..."); break; - + default: if (gnutls_error_is_fatal (ret) == 0) { LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret)); @@ -984,11 +1002,11 @@ again: LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret)); } } ); - + if (ret == 0) CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR), ); - -end: + +end: if (ret <= 0) fd_cnx_markerror(conn); return ret; @@ -1000,12 +1018,12 @@ static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t s ssize_t ret; struct timespec ts, now; CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), return -1 ); -again: +again: CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz), { pthread_testcancel(); switch (ret) { - case GNUTLS_E_REHANDSHAKE: + case GNUTLS_E_REHANDSHAKE: if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) { CHECK_GNUTLS_DO( ret = gnutls_handshake(session), { @@ -1034,10 +1052,10 @@ again: LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret)); } } ); -end: +end: if (ret <= 0) fd_cnx_markerror(conn); - + return ret; } @@ -1045,8 +1063,8 @@ end: /* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */ /* For the case of DTLS, since we are not using SCTP_UNORDERED, the messages over a single stream are ordered. Furthermore, as long as messages are shorter than the MTU [2^14 = 16384 bytes], they are delivered in a single - record, as far as I understand. - For larger messages, however, it is possible that pieces of messages coming from different streams can get interleaved. + record, as far as I understand. + For larger messages, however, it is possible that pieces of messages coming from different streams can get interleaved. As a result, we do not use the following function for DTLS reception, because we use the sequence number to rebuild the messages. */ int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session) @@ -1095,19 +1113,19 @@ int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session) } received += ret; } - + fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl); - + /* We have received a complete message, pass it to the daemon */ - CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), - { + CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), + { free_rcvdata(&rcv_data); CHECK_FCT_DO(fd_core_shutdown(), ); - return ret; + return ret; } ); - + } while (1); - + out: return (ret == 0) ? 0 : ENOTCONN; } @@ -1116,24 +1134,24 @@ out: static void * rcvthr_tls_single(void * arg) { struct cnxctx * conn = arg; - + TRACE_ENTRY("%p", arg); CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL ); - + /* Set the thread name */ { char buf[48]; snprintf(buf, sizeof(buf), "Receiver (%d) TLS/single stream", conn->cc_socket); fd_log_threadname ( buf ); } - + ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) ); ASSERT( fd_cnx_target_queue(conn) ); - /* The next function only returns when there is an error on the socket */ + /* The next function only returns when there is an error on the socket */ CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */); - TRACE_DEBUG(FULL, "Thread terminated"); + TRACE_DEBUG(FULL, "Thread terminated"); return NULL; } @@ -1151,7 +1169,7 @@ int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priori /* Set the algorithm suite */ if (priority) { const char * errorpos; - CHECK_GNUTLS_DO( gnutls_priority_set_direct( *session, priority, &errorpos ), + CHECK_GNUTLS_DO( gnutls_priority_set_direct( *session, priority, &errorpos ), { TRACE_DEBUG(INFO, "Error in priority string '%s' at position: '%s'", priority, errorpos); return EINVAL; } ); } else { CHECK_GNUTLS_DO( gnutls_priority_set( *session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL ); @@ -1164,7 +1182,7 @@ int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priori if (mode == GNUTLS_SERVER) { gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUIRE); } - + return 0; } @@ -1179,17 +1197,17 @@ int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, in unsigned int cert_list_size; gnutls_x509_crt_t cert; time_t now; - + TRACE_ENTRY("%p %d", conn, verbose); CHECK_PARAMS(conn); - + /* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */ #ifdef DEBUG if (verbose) { const char *tmp; gnutls_kx_algorithm_t kx; gnutls_credentials_type_t cred; - + LOG_D("TLS Session information for connection '%s':", conn->cc_id); /* print the key exchange's algorithm name */ @@ -1229,7 +1247,7 @@ int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, in gnutls_dh_get_prime_bits (session)); } break; -#ifdef ENABLE_SRP +#ifdef ENABLE_SRP case GNUTLS_CRD_SRP: LOG_D("\t - SRP session with username %s", gnutls_srp_server_get_username (session)); @@ -1263,7 +1281,7 @@ int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, in LOG_D("\t - MAC: %s", tmp); } #endif /* DEBUG */ - + /* First, use built-in verification */ CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, >ret), return EINVAL ); if (gtret) { @@ -1280,33 +1298,33 @@ int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, in LOG_E(" - The certificate signature uses a weak algorithm."); return EINVAL; } - + /* Code from http://www.gnu.org/software/gnutls/manual/gnutls.html#Verifying-peer_0027s-certificate */ if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) { LOG_E("TLS: Remote peer did not present a certificate, other mechanisms are not supported yet. socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id); return EINVAL; } - + GNUTLS_TRACE( cert_list = gnutls_certificate_get_peers (session, &cert_list_size) ); if (cert_list == NULL) return EINVAL; - + now = time(NULL); - + #ifdef DEBUG char serial[40]; char dn[128]; size_t size; unsigned int algo, bits; time_t expiration_time, activation_time; - + LOG_D("TLS Certificate information for connection '%s' (%d certs provided):", conn->cc_id, cert_list_size); for (i = 0; i < cert_list_size; i++) { CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL); CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL); - + LOG_A(" Certificate %d info:", i); GNUTLS_TRACE( expiration_time = gnutls_x509_crt_get_expiration_time (cert) ); @@ -1318,7 +1336,7 @@ int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, in /* Print the serial number of the certificate. */ size = sizeof (serial); gnutls_x509_crt_get_serial (cert, serial, &size); - + { int j; char buf[1024]; @@ -1354,24 +1372,24 @@ int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, in for (i = 0; i < cert_list_size; i++) { time_t deadline; - + CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL); CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL); - + GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(cert) ); if ((deadline != (time_t)-1) && (deadline < now)) { LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id); LOG_E(" - The certificate %d in the chain is expired", i); ret = EINVAL; } - + GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(cert) ); if ((deadline != (time_t)-1) && (deadline > now)) { LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id); LOG_E(" - The certificate %d in the chain is not yet activated", i); ret = EINVAL; } - + if ((i == 0) && (conn->cc_tls_para.cn)) { if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) { LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id); @@ -1379,7 +1397,7 @@ int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, in ret = EINVAL; } } - + GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) ); } @@ -1400,10 +1418,10 @@ int fd_tls_verify_credentials_2(gnutls_session_t session) int hostname_verified = 0; TRACE_ENTRY("%p", session); - + /* get the associated connection */ conn = gnutls_session_get_ptr (session); - + /* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */ #ifdef DEBUG const char *tmp; @@ -1414,7 +1432,7 @@ int fd_tls_verify_credentials_2(gnutls_session_t session) dhe = ecdh = 0; LOG_A("TLS Session information for connection '%s':", conn->cc_id); - + /* print the key exchange's algorithm name */ GNUTLS_TRACE( kx = gnutls_kx_get (session) ); @@ -1474,7 +1492,7 @@ int fd_tls_verify_credentials_2(gnutls_session_t session) dhe = 1; else if (kx == GNUTLS_KX_ECDHE_RSA || kx == GNUTLS_KX_ECDHE_ECDSA) ecdh = 1; - + /* Now print some info on the remote certificate */ if (gnutls_certificate_type_get (session) == GNUTLS_CRT_X509) { gnutls_datum_t cinfo; @@ -1504,7 +1522,7 @@ int fd_tls_verify_credentials_2(gnutls_session_t session) LOG_D("\t\t%s", cinfo.data); gnutls_free (cinfo.data); } - + if (conn->cc_tls_para.cn) { if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) { LOG_E("\tTLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id); @@ -1512,7 +1530,7 @@ int fd_tls_verify_credentials_2(gnutls_session_t session) gnutls_x509_crt_deinit (cert); return GNUTLS_E_CERTIFICATE_ERROR; } - + } hostname_verified = 1; @@ -1535,7 +1553,7 @@ int fd_tls_verify_credentials_2(gnutls_session_t session) LOG_D("\t - Ephemeral DH using prime of %d bits", gnutls_dh_get_prime_bits (session)); - /* print the protocol's name (ie TLS 1.0) + /* print the protocol's name (ie TLS 1.0) */ tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session)); LOG_D("\t - Protocol: %s", tmp); @@ -1557,8 +1575,8 @@ int fd_tls_verify_credentials_2(gnutls_session_t session) */ tmp = gnutls_mac_get_name (gnutls_mac_get (session)); LOG_D("\t - MAC: %s", tmp); - -#endif /* DEBUG */ + +#endif /* DEBUG */ /* This verification function uses the trusted CAs in the credentials * structure. So you must have installed one or more CA certificates. @@ -1577,12 +1595,12 @@ int fd_tls_verify_credentials_2(gnutls_session_t session) if (status & GNUTLS_CERT_NOT_ACTIVATED) LOG_E(" - The certificate is not yet activated."); - } + } if (status & GNUTLS_CERT_INVALID) { return GNUTLS_E_CERTIFICATE_ERROR; } - + /* Up to here the process is the same for X.509 certificates and * OpenPGP keys. From now on X.509 certificates are assumed. This can * be easily extended to work with openpgp keys as well. @@ -1634,22 +1652,22 @@ static int fd_cnx_uses_dtls(struct cnxctx * conn) { int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds) { int dtls = 0; - + TRACE_ENTRY( "%p %d %d %p %p", conn, mode, algo, priority, alt_creds); CHECK_PARAMS( conn && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) ); /* Save the mode */ conn->cc_tls_para.mode = mode; conn->cc_tls_para.algo = algo; - + /* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */ CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); - + /* Once TLS handshake is done, we don't stop after the first message */ conn->cc_loop = 1; - + dtls = fd_cnx_may_dtls(conn); - + /* Prepare the master session credentials and priority */ CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, dtls, priority, alt_creds) ); @@ -1682,32 +1700,32 @@ int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, return ENOTSUP; } } - + /* 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_tls_para.session, (void *) conn); - + if ((conn->cc_tls_para.cn != NULL) && (mode == GNUTLS_CLIENT)) { /* this might allow virtual hosting on the remote peer */ CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_tls_para.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_tls_para.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT)); #endif /* GNUTLS_VERSION_310 */ - + /* Mark the connection as protected from here, so that the gnutls credentials will be freed */ fd_cnx_addstate(conn, CC_STATUS_TLS); - + /* Handshake master session */ { int ret; - + CHECK_GNUTLS_DO( ret = gnutls_handshake(conn->cc_tls_para.session), { if (TRACE_BOOL(INFO)) { @@ -1719,15 +1737,15 @@ int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, #ifndef GNUTLS_VERSION_300 /* Now verify the remote credentials are valid -- only simple tests here */ - CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), - { + CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), + { CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR), ); fd_cnx_markerror(conn); return EINVAL; }); #endif /* GNUTLS_VERSION_300 */ } - + /* Multi-stream TLS: handshake other streams as well */ if ((!dtls) && (conn->cc_sctp_para.pairs > 1)) { #ifndef DISABLE_SCTP @@ -1750,7 +1768,7 @@ int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, return ENOTSUP; } } - + return 0; } @@ -1759,10 +1777,10 @@ int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsig { TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size); CHECK_PARAMS( conn && fd_cnx_teststate(conn, CC_STATUS_TLS) && cert_list && cert_list_size ); - + /* This function only works for X.509 certificates. */ CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 ); - + GNUTLS_TRACE( *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size) ); if (*cert_list == NULL) { TRACE_DEBUG(INFO, "No certificate was provided by remote peer / an error occurred."); @@ -1770,7 +1788,7 @@ int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsig } TRACE_DEBUG( FULL, "Saved certificate chain (%d certificates) in peer structure.", *cert_list_size); - + return 0; } @@ -1781,7 +1799,7 @@ int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned cha int ev; size_t ev_sz; void * ev_data; - + TRACE_ENTRY("%p %p %p %p", conn, timeout, buf, len); CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len); CHECK_PARAMS(conn->cc_rcvthr != (pthread_t)NULL); @@ -1794,27 +1812,27 @@ get_next: } else { CHECK_FCT( fd_event_get(conn->cc_incoming, &ev, &ev_sz, &ev_data) ); } - + switch (ev) { case FDEVP_CNX_MSG_RECV: /* We got one */ *len = ev_sz; *buf = ev_data; return 0; - + case FDEVP_PSM_TIMEOUT: TRACE_DEBUG(FULL, "Timeout event received"); return ETIMEDOUT; - + case FDEVP_CNX_EP_CHANGE: /* We ignore this event */ goto get_next; - + case FDEVP_CNX_ERROR: TRACE_DEBUG(FULL, "Received ERROR event on the connection"); return ENOTCONN; } - + TRACE_DEBUG(INFO, "Received unexpected event %d (%s)", ev, fd_pev_str(ev)); return EINVAL; } @@ -1835,12 +1853,12 @@ int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo) int ret; TRACE_ENTRY( "%p %p", conn, alt_fifo ); CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming ); - + /* The magic function does it all */ CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); CHECK_FCT_DO( ret = fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ), ); CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); - + return ret; } @@ -1861,7 +1879,7 @@ static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len) } if (ret <= 0) return ENOTCONN; - + sent += ret; } while ( sent < len ); return 0; @@ -1871,16 +1889,16 @@ static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len) int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len) { TRACE_ENTRY("%p %p %zd", conn, buf, len); - + CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! fd_cnx_teststate(conn, CC_STATUS_ERROR)) && buf && len); TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, fd_cnx_teststate(conn, CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id); - + switch (conn->cc_proto) { case IPPROTO_TCP: CHECK_FCT( send_simple(conn, buf, len) ); break; - + #ifndef DISABLE_SCTP case IPPROTO_SCTP: { int dtls = fd_cnx_uses_dtls(conn); @@ -1892,14 +1910,14 @@ int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len) limit = conn->cc_sctp_para.pairs; else limit = conn->cc_sctp_para.str_out; - + if (limit > 1) { conn->cc_sctp_para.next += 1; conn->cc_sctp_para.next %= limit; stream = conn->cc_sctp_para.next; } } - + if (stream == 0) { /* We can use default function, it sends over stream #0 */ CHECK_FCT( send_simple(conn, buf, len) ); @@ -1908,7 +1926,7 @@ int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len) struct iovec iov; iov.iov_base = buf; iov.iov_len = len; - + CHECK_SYS_DO( fd_sctp_sendstrv(conn, stream, &iov, 1), { fd_cnx_markerror(conn); return ENOTCONN; } ); } else { /* push the data to the appropriate session */ @@ -1932,13 +1950,13 @@ int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len) } break; #endif /* DISABLE_SCTP */ - + default: TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto); ASSERT(0); return ENOTSUP; /* or EINVAL... */ } - + return 0; } @@ -1951,11 +1969,11 @@ int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len) void fd_cnx_destroy(struct cnxctx * conn) { TRACE_ENTRY("%p", conn); - + CHECK_PARAMS_DO(conn, return); - + fd_cnx_addstate(conn, CC_STATUS_CLOSING); - + /* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */ if (fd_cnx_teststate(conn, CC_STATUS_TLS)) { #ifndef DISABLE_SCTP @@ -1985,7 +2003,7 @@ void fd_cnx_destroy(struct cnxctx * conn) GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) ); conn->cc_tls_para.session = NULL; } - + /* Destroy the wrapper (also stops the demux thread) */ fd_sctp3436_destroy(conn); @@ -2007,7 +2025,7 @@ void fd_cnx_destroy(struct cnxctx * conn) /* Cancel the receiver thread in case it did not already terminate */ CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ ); } - + /* Free the resources of the TLS session */ if (conn->cc_tls_para.session) { GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) ); @@ -2017,25 +2035,25 @@ void fd_cnx_destroy(struct cnxctx * conn) } #endif /* DISABLE_SCTP */ } - + /* Terminate the thread in case it is not done yet -- is there any such case left ?*/ CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ ); - + /* Shut the connection down */ if (conn->cc_socket > 0) { shutdown(conn->cc_socket, SHUT_RDWR); close(conn->cc_socket); conn->cc_socket = -1; } - + /* Empty and destroy FIFO list */ if (conn->cc_incoming) { fd_event_destroy( &conn->cc_incoming, free ); } - + /* Free the object */ free(conn); - + /* Done! */ return; } diff --git a/libfdcore/cnxctx.h b/libfdcore/cnxctx.h index dcc4bea..e6438bc 100644 --- a/libfdcore/cnxctx.h +++ b/libfdcore/cnxctx.h @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -43,14 +43,14 @@ /* 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_id[100]; /* 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 @@ -59,7 +59,7 @@ struct cnxctx { 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. */ @@ -117,7 +117,7 @@ int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl); /* 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_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list, struct fd_list * src_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 ); diff --git a/libfdcore/config.c b/libfdcore/config.c index d05c8ba..dae6c6c 100644 --- a/libfdcore/config.c +++ b/libfdcore/config.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -59,7 +59,13 @@ int fd_conf_init() fd_g_config->cnf_port_tls = DIAMETER_SECURE_PORT; fd_g_config->cnf_sctp_str = 30; fd_g_config->cnf_thr_srv = 5; + fd_g_config->cnf_processing_peers_minimum = 0; fd_g_config->cnf_dispthr = 4; + fd_g_config->cnf_rtinthr = 1; + fd_g_config->cnf_rtoutthr = 1; + fd_g_config->cnf_qin_limit = 20; + fd_g_config->cnf_qout_limit = 30; + fd_g_config->cnf_qlocal_limit = 25; fd_list_init(&fd_g_config->cnf_endpoints, NULL); fd_list_init(&fd_g_config->cnf_apps, NULL); #ifdef DISABLE_SCTP @@ -101,11 +107,18 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_conf_dump) 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); + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Minimal processing peers : %d\n", fd_g_config->cnf_processing_peers_minimum), return NULL); + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of rtin threads . : %hu\n", fd_g_config->cnf_rtinthr), return NULL); + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of rtout threads : %hu\n", fd_g_config->cnf_rtoutthr), return NULL); + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Incoming queue limit : %d\n", fd_g_config->cnf_qin_limit), return NULL); + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Outgoing queue limit : %d\n", fd_g_config->cnf_qout_limit), return NULL); + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local queue limit : %d\n", fd_g_config->cnf_qlocal_limit), 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); + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), 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); @@ -134,6 +147,7 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_conf_dump) #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, " - Client bind .. : %s\n", fd_g_config->cnf_flags.no_bind ? "DISABLED" : "Enabled"), 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); @@ -338,7 +352,8 @@ int fd_conf_parse() } /* 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)) { + fd_g_config->cnf_flags.no_bind = FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints); + if ((!fd_g_config->cnf_flags.no_bind) && (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; diff --git a/libfdcore/core.c b/libfdcore/core.c index 078e7fd..17d4326 100644 --- a/libfdcore/core.c +++ b/libfdcore/core.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2019, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -315,6 +315,8 @@ static int fd_core_start_int(void) int fd_core_start(void) { int ret; + CHECK_FCT( fd_queues_init_after_conf() ); + CHECK_POSIX( pthread_mutex_lock(&core_lock) ); ret = fd_core_start_int(); CHECK_POSIX( pthread_mutex_unlock(&core_lock) ); diff --git a/libfdcore/dict_base_proto.c b/libfdcore/dict_base_proto.c index 5c023f6..5eb7c58 100644 --- a/libfdcore/dict_base_proto.c +++ b/libfdcore/dict_base_proto.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -1582,7 +1582,37 @@ int fd_dict_base_protocol(struct dictionary * dict) }; CHECK_dict_new( DICT_AVP, &data , NULL, NULL); } - + + /* Experimental-Result-Code */ + { + /* + The Experimental-Result-Code AVP (AVP Code 298) is of type Unsigned32 + and contains a vendor-assigned value representing the result of + processing the request. + + It is recommended that vendor-specific result codes follow the same + conventions given for the Result-Code AVP regarding the different + types of result codes and the handling of errors (for non 2xxx + values). + */ + /* Although the RFC does not specify an "Enumerated" type here, we go forward and create one. + * This is the reason for the "*" in the type name. Vendors will have to define their values. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_UNSIGNED32, "Enumerated(Experimental-Result-Code)" , NULL, NULL, NULL }; + struct dict_avp_data data = { + 298, /* Code */ + 0, /* Vendor */ + "Experimental-Result-Code", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + /* Experimental-Result */ { /* @@ -1594,8 +1624,8 @@ int fd_dict_base_protocol(struct dictionary * dict) AVP Format Experimental-Result ::= < AVP Header: 297 > - { Vendor-Id } - { Experimental-Result-Code } + { Vendor-Id } + { Experimental-Result-Code } The Vendor-Id AVP (see Section 5.3.3) in this grouped AVP identifies the vendor responsible for the assignment of the result code which @@ -1603,47 +1633,23 @@ int fd_dict_base_protocol(struct dictionary * dict) applications MUST include either one Result-Code AVP or one Experimental-Result AVP. */ - struct dict_avp_data data = { - 297, /* Code */ - 0, /* Vendor */ - "Experimental-Result", /* Name */ - AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + struct dict_object * avp = NULL; + struct dict_avp_data data = { + 297, /* Code */ + 0, /* Vendor */ + "Experimental-Result", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ AVP_FLAG_MANDATORY, /* Fixed flag values */ - AVP_TYPE_GROUPED /* base type of data */ + AVP_TYPE_GROUPED /* base type of data */ }; - CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + struct local_rules_definition rules[] = { + { "Vendor-Id", RULE_REQUIRED, -1, 1 }, + { "Experimental-Result-Code", RULE_REQUIRED, -1, 1 }, + }; + CHECK_dict_new( DICT_AVP, &data , NULL, &avp); + PARSE_loc_rules( rules, avp ); } - - /* Experimental-Result-Code */ - { - /* - The Experimental-Result-Code AVP (AVP Code 298) is of type Unsigned32 - and contains a vendor-assigned value representing the result of - processing the request. - It is recommended that vendor-specific result codes follow the same - conventions given for the Result-Code AVP regarding the different - types of result codes and the handling of errors (for non 2xxx - values). - */ - /* Although the RFC does not specify an "Enumerated" type here, we go forward and create one. - * This is the reason for the "*" in the type name. Vendors will have to define their values. - */ - struct dict_object * type; - struct dict_type_data tdata = { AVP_TYPE_UNSIGNED32, "Enumerated(Experimental-Result-Code)" , NULL, NULL, NULL }; - struct dict_avp_data data = { - 298, /* Code */ - 0, /* Vendor */ - "Experimental-Result-Code", /* Name */ - AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ - AVP_FLAG_MANDATORY, /* Fixed flag values */ - AVP_TYPE_UNSIGNED32 /* base type of data */ - }; - - CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); - CHECK_dict_new( DICT_AVP, &data , type, NULL); - } - /* Auth-Request-Type */ { /* diff --git a/libfdcore/fdcore-internal.h b/libfdcore/fdcore-internal.h index 7fac852..03d6eed 100644 --- a/libfdcore/fdcore-internal.h +++ b/libfdcore/fdcore-internal.h @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -109,6 +109,7 @@ extern struct fifo * fd_g_outgoing; /* messages to be sent to other peers on the extern struct fifo * fd_g_local; /* messages to be handled to local extensions */ /* Message queues */ int fd_queues_init(void); +int fd_queues_init_after_conf(void); int fd_queues_fini(struct fifo ** queue); /* Trigged events */ @@ -341,7 +342,7 @@ 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); +struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list, struct fd_list * src_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); diff --git a/libfdcore/fdd.l b/libfdcore/fdd.l index 8835def..348ae74 100644 --- a/libfdcore/fdd.l +++ b/libfdcore/fdd.l @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2019, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -241,34 +241,44 @@ nomatch: } /* 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; } +(?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:"RoutingInThreads") { return ROUTINGINTHREADS; } +(?i:"RoutingOutThreads") { return ROUTINGOUTTHREADS; } +(?i:"IncomingQueueLimit") { return QINLIMIT; } +(?i:"OutgoingQueueLimit") { return QOUTLIMIT; } +(?i:"LocalQueueLimit") { return QLOCALLIMIT; } +(?i:"ListenOn") { return LISTENON; } +(?i:"ThreadsPerServer") { return THRPERSRV; } +(?i:"ProcessingPeersPattern") { return PROCESSINGPEERSPATTERN; } +(?i:"ProcessingPeersMinimum") { return PROCESSINGPEERSMINIMUM; } +(?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; } +(?i:"RouteRecordInAnswers") { return RR_IN_ANSWERS; } +(?i:"Never") { return NEVER; } +(?i:"Always") { return ALWAYS; } /* Valid single characters for yyparse */ diff --git a/libfdcore/fdd.y b/libfdcore/fdd.y index db27243..9b36887 100644 --- a/libfdcore/fdd.y +++ b/libfdcore/fdd.y @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2019, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -107,8 +107,15 @@ struct peer_info fddpi; %token NOTLS %token SCTPSTREAMS %token APPSERVTHREADS +%token ROUTINGINTHREADS +%token ROUTINGOUTTHREADS +%token QINLIMIT +%token QOUTLIMIT +%token QLOCALLIMIT %token LISTENON %token THRPERSRV +%token PROCESSINGPEERSPATTERN +%token PROCESSINGPEERSMINIMUM %token TCTIMER %token TWTIMER %token NORELAY @@ -141,8 +148,15 @@ conffile: /* Empty is OK -- for simplicity here, we reject in daemon later */ | conffile sctpstreams | conffile listenon | conffile thrpersrv + | conffile processingpeerspattern + | conffile processingpeersminimum | conffile norelay | conffile appservthreads + | conffile routinginthreads + | conffile routingoutthreads + | conffile qinlimit + | conffile qoutlimit + | conffile qlocallimit | conffile noip | conffile noip6 | conffile notcp @@ -252,6 +266,45 @@ thrpersrv: THRPERSRV '=' INTEGER ';' } ; +processingpeerspattern: PROCESSINGPEERSPATTERN '=' QSTRING ';' + { + char *pattern = $3; + int err; + CHECK_FCT_DO( err=regcomp(&conf->cnf_processing_peers_pattern_regex, pattern, REG_EXTENDED | REG_NOSUB), + { + char * buf; + size_t bl; + + /* Error while compiling the regex */ + TRACE_DEBUG(INFO, "error while compiling the regular expression '%s':", pattern); + + /* Get the error message size */ + bl = regerror(err, &conf->cnf_processing_peers_pattern_regex, NULL, 0); + + /* Alloc the buffer for error message */ + CHECK_MALLOC( buf = malloc(bl) ); + + /* Get the error message content */ + regerror(err, &conf->cnf_processing_peers_pattern_regex, buf, bl); + TRACE_DEBUG(INFO, "\t%s", buf); + + /* Free the buffer, return the error */ + free(buf); + + yyerror (&yylloc, conf, "Invalid regular expression in ProcessingPeersPattern"); + YYERROR; + } ); + } + ; + +processingpeersminimum: PROCESSINGPEERSMINIMUM '=' INTEGER ';' + { + CHECK_PARAMS_DO( ($3 >= 0), + { yyerror (&yylloc, conf, "Invalid value"); YYERROR; } ); + conf->cnf_processing_peers_minimum = $3; + } + ; + norelay: NORELAY ';' { conf->cnf_flags.no_fwd = 1; @@ -266,6 +319,46 @@ appservthreads: APPSERVTHREADS '=' INTEGER ';' } ; +routinginthreads: ROUTINGINTHREADS '=' INTEGER ';' + { + CHECK_PARAMS_DO( ($3 > 0) && ($3 < 256), + { yyerror (&yylloc, conf, "Invalid value"); YYERROR; } ); + conf->cnf_rtinthr = (uint16_t)$3; + } + ; + +routingoutthreads: ROUTINGOUTTHREADS '=' INTEGER ';' + { + CHECK_PARAMS_DO( ($3 > 0) && ($3 < 256), + { yyerror (&yylloc, conf, "Invalid value"); YYERROR; } ); + conf->cnf_rtoutthr = (uint16_t)$3; + } + ; + +qinlimit: QINLIMIT '=' INTEGER ';' + { + CHECK_PARAMS_DO( ($3 >= 0), + { yyerror (&yylloc, conf, "Invalid value"); YYERROR; } ); + conf->cnf_qin_limit = $3; + } + ; + +qoutlimit: QOUTLIMIT '=' INTEGER ';' + { + CHECK_PARAMS_DO( ($3 >= 0), + { yyerror (&yylloc, conf, "Invalid value"); YYERROR; } ); + conf->cnf_qout_limit = $3; + } + ; + +qlocallimit: QLOCALLIMIT '=' INTEGER ';' + { + CHECK_PARAMS_DO( ($3 >= 0), + { yyerror (&yylloc, conf, "Invalid value"); YYERROR; } ); + conf->cnf_qlocal_limit = $3; + } + ; + noip: NOIP ';' { if (got_peer_noipv6) { diff --git a/libfdcore/hooks.c b/libfdcore/hooks.c index 885be86..b812649 100644 --- a/libfdcore/hooks.c +++ b/libfdcore/hooks.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -332,7 +332,9 @@ void fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * p /* There was no registered handler, default behavior for this hook */ switch (type) { case HOOK_DATA_RECEIVED: { +#ifdef DEBUG struct fd_cnx_rcvdata *rcv_data = other; +#endif LOG_A("RCV: %zd bytes", rcv_data->length); break; } diff --git a/libfdcore/messages.c b/libfdcore/messages.c index c13bfff..e0f8a56 100644 --- a/libfdcore/messages.c +++ b/libfdcore/messages.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -42,6 +42,9 @@ 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 */ +static struct dict_object * dict_avp_ER = NULL; /* Experimental-Result */ +static struct dict_object * dict_avp_VI = NULL; /* Vendor-Id */ +static struct dict_object * dict_avp_ERC = NULL; /* Experimental-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 */ @@ -52,25 +55,28 @@ struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */ 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, "Experimental-Result", &dict_avp_ER, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dict_avp_VI, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Experimental-Result-Code", &dict_avp_ERC, 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; } @@ -81,35 +87,35 @@ int fd_msg_add_origin ( struct msg * msg, int osi ) 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 ) ); @@ -122,7 +128,7 @@ int fd_msg_add_origin ( struct msg * msg, int osi ) /* Add it to the message */ CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) ); } - + return 0; } @@ -134,93 +140,127 @@ int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen ) 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 ) +/* Add Result-Code or Experimental-Result, and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */ +int fd_msg_add_result( struct msg * msg, vendor_id_t vendor, struct dict_object * restype, 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 ); - + + TRACE_ENTRY("%p %d %p %s %p %p %d", msg, vendor, restype, rescode, errormsg, optavp, type_id); + + CHECK_PARAMS( msg && restype && rescode ); + /* Find the enum value corresponding to the rescode string, this will give the class of error */ { struct dict_object * enum_obj = NULL; + + /* Search in the restype */ 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 ) ); - + req.type_obj = restype; + /* 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 (vendor == 0) { + /* Vendor 0; create the Result-Code AVP */ + struct avp * avp_RC = NULL; + 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 ) ); + } else { + /* Vendor !0; create the Experimental-Result AVP */ + struct avp * avp_ER = NULL; + CHECK_FCT( fd_msg_avp_new( dict_avp_ER, 0, &avp_ER ) ); + + /* Create the Vendor-Id AVP and add to Experimental-Result */ + { + struct avp * avp_VI = NULL; + CHECK_FCT( fd_msg_avp_new( dict_avp_VI, 0, &avp_VI ) ); + + /* Set Vendor-Id value to vendor */ + memset(&val, 0, sizeof(val)); + val.u32 = vendor; + CHECK_FCT( fd_msg_avp_setvalue( avp_VI, &val ) ); + + /* Add it to Experimental-Result */ + CHECK_FCT( fd_msg_avp_add( avp_ER, MSG_BRW_LAST_CHILD, avp_VI ) ); + } + + /* Create the Experimental-Result-Code AVP and add to Experimental-Result */ + { + struct avp * avp_ERC = NULL; + CHECK_FCT( fd_msg_avp_new( dict_avp_ERC, 0, &avp_ERC ) ); + + /* Set Experimental-Result-Code value to rc_val */ + memset(&val, 0, sizeof(val)); + val.u32 = rc_val; + CHECK_FCT( fd_msg_avp_setvalue( avp_ERC, &val ) ); + + /* Add it to Experimental-Result */ + CHECK_FCT( fd_msg_avp_add( avp_ER, MSG_BRW_LAST_CHILD, avp_ERC ) ); + } + + /* Add it to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ER ) ); + } + if (type_id == 2) { /* Add the Error-Reporting-Host AVP */ - + struct avp * avp_ERH = NULL; CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) ); /* Set its value */ @@ -231,60 +271,60 @@ int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struc /* 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 */ + + /* Now add the optavp in a Failed-AVP if provided */ if (optavp) { + struct avp * avp_FAVP= NULL; 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) + 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 */ if (rc_val == 5017) /* DIAMETER_NO_COMMON_SECURITY */ { @@ -293,28 +333,28 @@ int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struc 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 */ - + struct avp * avp_EM = NULL; 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); @@ -327,18 +367,25 @@ int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struc /* Add it to the message */ CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) ); } - + return 0; } +int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id ) +{ + struct dict_object * restype = NULL; + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &restype, ENOENT ) ); + return fd_msg_add_result(msg, 0, restype, rescode, errormsg, optavp, type_id); +} + 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) @@ -346,10 +393,10 @@ static int fd_msg_send_int( struct msg ** pmsg, void (*anscb)(void *, struct msg && (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; } @@ -358,7 +405,7 @@ int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void { TRACE_ENTRY("%p %p %p", pmsg, anscb, data); CHECK_PARAMS( pmsg ); - + return fd_msg_send_int(pmsg, anscb, data, NULL, NULL); } @@ -367,7 +414,7 @@ int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg * { 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); } @@ -379,70 +426,78 @@ int fd_msg_parse_or_error( struct msg ** msg, struct msg **error) 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 ); + if (fd_msg_parse_dict(avp, fd_g_config->cnf_dict, &pei) < 0) { + TRACE_DEBUG(INFO, "error parsing Result-Code AVP"); + rc = NULL; + break; + } rc = ahdr->avp_value; + if (rc == NULL) { + TRACE_DEBUG(INFO, "invalid Result-Code AVP"); + break; + } 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; @@ -450,6 +505,6 @@ int fd_msg_parse_or_error( struct msg ** msg, struct msg **error) } } while (0); } - + return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */ } diff --git a/libfdcore/p_ce.c b/libfdcore/p_ce.c index 1b4cd13..3f3af76 100644 --- a/libfdcore/p_ce.c +++ b/libfdcore/p_ce.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -704,7 +704,7 @@ int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator) int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer) { uint32_t rc = 0; - int st; + int st = STATE_NEW; struct fd_pei pei; TRACE_ENTRY("%p %p", msg, peer); @@ -833,6 +833,26 @@ cleanup: return 0; } +/* Check if enough processing peers are connected to allow connections by other peers */ +static int sufficient_processing_peers(void) { + int processing_peers_count = 0; + struct fd_list * li; + + CHECK_FCT( pthread_rwlock_rdlock(&fd_g_activ_peers_rw) ); + for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) { + struct fd_peer * p = (struct fd_peer *)li->o; + + TRACE_DEBUG(FULL, "comparing '%s' against processing peers pattern", p->p_hdr.info.pi_diamid); + if (regexec(&fd_g_config->cnf_processing_peers_pattern_regex, p->p_hdr.info.pi_diamid, 0, NULL, 0) == 0) { + processing_peers_count++; + } + } + CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) ); + + TRACE_DEBUG(FULL, "%d processing peers found", processing_peers_count); + return (processing_peers_count >= fd_g_config->cnf_processing_peers_minimum); +} + /* Handle the receiver side to go to OPEN or OPEN_NEW state (any election is resolved) */ int fd_p_ce_process_receiver(struct fd_peer * peer) { @@ -883,6 +903,26 @@ int fd_p_ce_process_receiver(struct fd_peer * peer) CHECK_FCT( res ); } + /* Check peer type and if enough processing peers are already connected */ + if (fd_g_config->cnf_processing_peers_minimum > 0) { + if (regexec(&fd_g_config->cnf_processing_peers_pattern_regex, peer->p_hdr.info.pi_diamid, 0, NULL, 0) != 0) { + /* peer is not a processing peer */ + if (!sufficient_processing_peers()) { + pei.pei_errcode = "DIAMETER_TOO_BUSY"; + goto error_abort; + } + } + } + + if (peer->p_flags.pf_responder) { + int res = fd_peer_validate( peer ); + if (res < 0) { + TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).", peer->p_hdr.info.pi_diamid); + pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; + goto error_abort; + } + CHECK_FCT( res ); + } /* Check if we have common applications */ if ( fd_g_config->cnf_flags.no_fwd && (! peer->p_hdr.info.runtime.pir_relay) ) { int got_common; diff --git a/libfdcore/p_cnx.c b/libfdcore/p_cnx.c index 9dc2a03..009e73d 100644 --- a/libfdcore/p_cnx.c +++ b/libfdcore/p_cnx.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -261,12 +261,14 @@ static void * connect_thr(void * arg) switch (nc->proto) { case IPPROTO_TCP: +/* TODO: use no_bind and first of cnf_endpoints of nc->ss.sa_family ? */ 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); + nc->port, &peer->p_hdr.info.pi_endpoints, + fd_g_config->cnf_flags.no_bind ? NULL : &fd_g_config->cnf_endpoints); break; #endif /* DISABLE_SCTP */ } diff --git a/libfdcore/p_out.c b/libfdcore/p_out.c index b410f11..ef4f6a5 100644 --- a/libfdcore/p_out.c +++ b/libfdcore/p_out.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2016, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * diff --git a/libfdcore/p_psm.c b/libfdcore/p_psm.c index d2a9bbd..755e133 100644 --- a/libfdcore/p_psm.c +++ b/libfdcore/p_psm.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2019, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -40,16 +40,16 @@ 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 +beginning 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?) +Personally 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 @@ -96,8 +96,8 @@ 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 +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 @@ -127,7 +127,7 @@ static int fd_psm_waitstart() int ret = 0; TRACE_ENTRY(""); CHECK_POSIX( pthread_mutex_lock(&started_mtx) ); -awake: +awake: if (!ret && !started) { pthread_cleanup_push( fd_cleanup_mutex, &started_mtx ); CHECK_POSIX_DO( ret = pthread_cond_wait(&started_cnd, &started_mtx), ); @@ -159,7 +159,7 @@ 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), @@ -170,19 +170,19 @@ static int enter_open_state(struct fd_peer * peer) 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, + 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) { TRACE_DEBUG(FULL, "Calling add callback for peer %s", peer->p_hdr.info.pi_diamid); @@ -190,13 +190,13 @@ static int enter_open_state(struct fd_peer * peer) 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) @@ -205,15 +205,15 @@ static int leave_open_state(struct fd_peer * peer, int skip_failover) 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; } @@ -233,11 +233,11 @@ void fd_psm_events_free(struct fd_peer * peer) 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)); @@ -255,16 +255,16 @@ void fd_psm_events_free(struct fd_peer * peer) 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; } @@ -273,24 +273,24 @@ int fd_peer_get_state(struct peer_hdr *peer) 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)) ? ((new_state == STATE_SUSPECT || new_state == STATE_CLOSED) ? FD_LOG_ERROR : 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) ); } @@ -301,20 +301,20 @@ int fd_psm_change_state(struct fd_peer * peer, int new_state) 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 */ + + /* Reset the counter of pending answers to send */ peer->p_reqin_count = 0; - - /* If the peer is not persistant, we destroy it */ + + /* If the peer is not persistent, 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; } @@ -322,10 +322,10 @@ int fd_psm_change_state(struct fd_peer * peer, int new_state) 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; @@ -340,9 +340,9 @@ void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay) 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; @@ -356,29 +356,29 @@ void fd_psm_cleanup(struct fd_peer * peer, int terminate) 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) +/* Cancellation 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 ); @@ -388,7 +388,7 @@ void cleanup_setstate(void * arg) return; } -/* The state machine thread (controler) */ +/* The state machine thread (controller) */ static void * p_psm_th( void * arg ) { struct fd_peer * peer = (struct fd_peer *)arg; @@ -397,18 +397,18 @@ static void * p_psm_th( void * arg ) 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; @@ -416,24 +416,24 @@ static void * p_psm_th( void * arg ) /* 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, @@ -462,8 +462,8 @@ psm_loop: /* 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: @@ -477,30 +477,30 @@ psm_loop: 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), + 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 */ @@ -508,10 +508,10 @@ psm_loop: 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; @@ -522,20 +522,20 @@ psm_loop: 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) { @@ -562,7 +562,7 @@ psm_loop: 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 ); @@ -571,7 +571,7 @@ psm_loop: 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: @@ -587,19 +587,19 @@ psm_loop: } 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, + 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); + 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; + fd_msg_free(msg); + goto psm_end; } ); } else { if (msg == NULL) { @@ -608,7 +608,7 @@ psm_loop: 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); + 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); } @@ -623,29 +623,29 @@ psm_loop: 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), + 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: 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); @@ -664,7 +664,7 @@ psm_loop: /* 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]; @@ -674,7 +674,7 @@ psm_loop: msg = NULL; } }; - + /* At this point the message must have been fully handled already */ if (msg) { char buf[256]; @@ -682,10 +682,10 @@ psm_loop: 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) { @@ -695,7 +695,7 @@ psm_loop: /* 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: @@ -705,43 +705,43 @@ psm_loop: 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; @@ -749,19 +749,19 @@ psm_loop: 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(¶ms->cer, peer, ¶ms->cnx, params->validate), goto psm_end ); - + /* Cleanup if needed */ if (params->cnx) { fd_cnx_destroy(params->cnx); @@ -771,43 +771,43 @@ psm_loop: 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 */ @@ -815,20 +815,20 @@ psm_loop: /* 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) { @@ -837,14 +837,14 @@ psm_loop: 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; @@ -855,29 +855,29 @@ psm_loop: /* 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: @@ -885,7 +885,7 @@ psm_reset: goto psm_end; fd_psm_cleanup(peer, 0); goto psm_loop; - + psm_end: cur_state = fd_peer_getstate(peer); if ((cur_state == STATE_CLOSING) || (cur_state == STATE_CLOSING_GRACE)) { @@ -911,16 +911,16 @@ psm_end: 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 */ + + /* Create the PSM controller thread */ CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) ); - + /* We're done */ return 0; } @@ -930,7 +930,7 @@ 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 { @@ -943,16 +943,16 @@ int fd_psm_terminate(struct fd_peer * peer, char * reason ) 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; } diff --git a/libfdcore/peers.c b/libfdcore/peers.c index 98f1921..5ac9ccf 100644 --- a/libfdcore/peers.c +++ b/libfdcore/peers.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2019, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -40,7 +40,7 @@ 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 */ +struct fd_list fd_g_activ_peers = FD_LIST_INITIALIZER(fd_g_activ_peers); /* peers linked by their p_actives ordered by p_diamid */ pthread_rwlock_t fd_g_activ_peers_rw = PTHREAD_RWLOCK_INITIALIZER; /* List of validation callbacks (registered with fd_peer_validate_register) */ diff --git a/libfdcore/queues.c b/libfdcore/queues.c index b317d34..561eaec 100644 --- a/libfdcore/queues.c +++ b/libfdcore/queues.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2019, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -44,9 +44,19 @@ struct fifo * fd_g_local = NULL; int fd_queues_init(void) { TRACE_ENTRY(); - CHECK_FCT( fd_fifo_new ( &fd_g_incoming, 20 ) ); - CHECK_FCT( fd_fifo_new ( &fd_g_outgoing, 30 ) ); - CHECK_FCT( fd_fifo_new ( &fd_g_local, 25 ) ); + CHECK_FCT( fd_fifo_new ( &fd_g_incoming, fd_g_config->cnf_qin_limit ) ); + CHECK_FCT( fd_fifo_new ( &fd_g_outgoing, fd_g_config->cnf_qout_limit ) ); + CHECK_FCT( fd_fifo_new ( &fd_g_local, fd_g_config->cnf_qlocal_limit ) ); + return 0; +} + +/* Resize according to values given in configuration file */ +int fd_queues_init_after_conf(void) +{ + TRACE_ENTRY(); + CHECK_FCT( fd_fifo_set_max ( fd_g_incoming, fd_g_config->cnf_qin_limit ) ); + CHECK_FCT( fd_fifo_set_max ( fd_g_outgoing, fd_g_config->cnf_qout_limit ) ); + CHECK_FCT( fd_fifo_set_max ( fd_g_local, fd_g_config->cnf_qlocal_limit ) ); return 0; } diff --git a/libfdcore/routing_dispatch.c b/libfdcore/routing_dispatch.c index a147d79..20d8e7f 100644 --- a/libfdcore/routing_dispatch.c +++ b/libfdcore/routing_dispatch.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2019, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -35,6 +35,13 @@ #include "fdcore-internal.h" +#ifdef linux +/* This needs -D_USE_GNU, and since I have no idea what else that does, let's simply copy the declaration. */ + +/* Set thread name visible in the kernel and its interfaces. */ +extern int pthread_setname_np (pthread_t __target_thread, const char *__name); +#endif + /********************************************************************************/ /* First part : handling the extensions callbacks */ /********************************************************************************/ @@ -1152,28 +1159,44 @@ static void * routing_out_thr(void * arg) static pthread_t * dispatch = NULL; static enum thread_state * disp_state = NULL; -/* Later: make this more dynamic */ -static pthread_t rt_out = (pthread_t)NULL; -static enum thread_state out_state = NOTRUNNING; +static pthread_t * rt_out = NULL; +static enum thread_state * out_state = NULL; -static pthread_t rt_in = (pthread_t)NULL; -static enum thread_state in_state = NOTRUNNING; +static pthread_t * rt_in = NULL; +static enum thread_state * in_state = NULL; /* Initialize the routing and dispatch threads */ int fd_rtdisp_init(void) { int i; - /* Prepare the array for dispatch */ + /* Prepare the array for threads */ CHECK_MALLOC( disp_state = calloc(fd_g_config->cnf_dispthr, sizeof(enum thread_state)) ); CHECK_MALLOC( dispatch = calloc(fd_g_config->cnf_dispthr, sizeof(pthread_t)) ); + CHECK_MALLOC( out_state = calloc(fd_g_config->cnf_rtoutthr, sizeof(enum thread_state)) ); + CHECK_MALLOC( rt_out = calloc(fd_g_config->cnf_rtoutthr, sizeof(pthread_t)) ); + CHECK_MALLOC( in_state = calloc(fd_g_config->cnf_rtinthr, sizeof(enum thread_state)) ); + CHECK_MALLOC( rt_in = calloc(fd_g_config->cnf_rtinthr, sizeof(pthread_t)) ); /* Create the threads */ for (i=0; i < fd_g_config->cnf_dispthr; i++) { CHECK_POSIX( pthread_create( &dispatch[i], NULL, dispatch_thr, &disp_state[i] ) ); +#ifdef linux + pthread_setname_np(dispatch[i], "fd-dispatch"); +#endif + } + for (i=0; i < fd_g_config->cnf_rtoutthr; i++) { + CHECK_POSIX( pthread_create( &rt_out[i], NULL, routing_out_thr, &out_state[i] ) ); +#ifdef linux + pthread_setname_np(rt_out[i], "fd-routing-out"); +#endif + } + for (i=0; i < fd_g_config->cnf_rtinthr; i++) { + CHECK_POSIX( pthread_create( &rt_in[i], NULL, routing_in_thr, &in_state[i] ) ); +#ifdef linux + pthread_setname_np(rt_in[i], "fd-routing-in"); +#endif } - CHECK_POSIX( pthread_create( &rt_out, NULL, routing_out_thr, &out_state) ); - CHECK_POSIX( pthread_create( &rt_in, NULL, routing_in_thr, &in_state) ); /* Later: TODO("Set the thresholds for the queues to create more threads as needed"); */ @@ -1244,13 +1267,33 @@ int fd_rtdisp_fini(void) CHECK_FCT_DO( fd_queues_fini(&fd_g_incoming), /* ignore */); /* Stop the routing IN thread */ - stop_thread_delayed(&in_state, &rt_in, "IN routing"); + if (rt_in != NULL) { + for (i=0; i < fd_g_config->cnf_rtinthr; i++) { + stop_thread_delayed(&in_state[i], &rt_in[i], "IN routing"); + } + free(rt_in); + rt_in = NULL; + } + if (in_state != NULL) { + free(in_state); + in_state = NULL; + } /* Destroy the outgoing queue */ CHECK_FCT_DO( fd_queues_fini(&fd_g_outgoing), /* ignore */); /* Stop the routing OUT thread */ - stop_thread_delayed(&out_state, &rt_out, "OUT routing"); + if (rt_out != NULL) { + for (i=0; i < fd_g_config->cnf_rtinthr; i++) { + stop_thread_delayed(&out_state[i], &rt_out[i], "OUT routing"); + } + free(rt_out); + rt_out = NULL; + } + if (out_state != NULL) { + free(out_state); + out_state = NULL; + } /* Destroy the local queue */ CHECK_FCT_DO( fd_queues_fini(&fd_g_local), /* ignore */); diff --git a/libfdcore/sctp.c b/libfdcore/sctp.c index cf7974a..9305b31 100644 --- a/libfdcore/sctp.c +++ b/libfdcore/sctp.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -48,7 +48,7 @@ /* #define OLD_SCTP_SOCKET_API */ /* Automatically fallback to old API if some of the new symbols are not defined */ -#if (!defined(SCTP_CONNECTX_4_ARGS) || (!defined(SCTP_RECVRCVINFO)) || (!defined(SCTP_SNDINFO))) +#if (!defined(SCTP_CONNECTX_4_ARGS) || (!defined(SCTP_RECVRCVINFO)) || (!defined(SCTP_SNDINFO)) || (!defined(SCTP_SEND_FAILED_EVENT))) # define OLD_SCTP_SOCKET_API #endif @@ -58,6 +58,34 @@ #define ADJUST_RTX_PARAMS #endif /* USE_DEFAULT_SCTP_RTX_PARAMS */ + +DECLARE_FD_DUMP_PROTOTYPE(fd_sa_dump_array, sSA * saddrs, int saddrs_count) +{ + union { + sSA *sa; + uint8_t *buf; + } ptr; + int i; + int salen; + + FD_DUMP_HANDLE_OFFSET(); + + ptr.sa = saddrs; + for (i = 0; i < saddrs_count; i++) { + salen = sSAlen(ptr.sa); + if (salen == 0) { + LOG_E("fd_sa_dump_array: Unknown sockaddr family"); + break; + } + if (i > 0) { + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " "), return NULL); + } + CHECK_MALLOC_DO( fd_sa_dump( FD_DUMP_STD_PARAMS, ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV), return NULL); + ptr.buf += salen; + } + return *buf; +} + /* Pre-binding socket options -- # streams read in config */ static int fd_setsockopt_prebind(int sk) { @@ -82,7 +110,7 @@ static int fd_setsockopt_prebind(int sk) if (TRACE_BOOL(ANNOYING)) { sz = sizeof(rtoinfo); /* Read socket defaults */ - CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz) ); + CHECK_SYS( sctp_opt_info(sk, 0, SCTP_RTOINFO, &rtoinfo, &sz) ); if (sz != sizeof(rtoinfo)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo)); @@ -102,7 +130,7 @@ static int fd_setsockopt_prebind(int sk) if (TRACE_BOOL(ANNOYING)) { /* Check new values */ - CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz) ); + CHECK_SYS( sctp_opt_info(sk, 0, SCTP_RTOINFO, &rtoinfo, &sz) ); fd_log_debug( "New SCTP_RTOINFO : srto_initial : %u", rtoinfo.srto_initial); fd_log_debug( " srto_max : %u", rtoinfo.srto_max); fd_log_debug( " srto_min : %u", rtoinfo.srto_min); @@ -121,7 +149,7 @@ static int fd_setsockopt_prebind(int sk) if (TRACE_BOOL(ANNOYING)) { sz = sizeof(assoc); /* Read socket defaults */ - CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz) ); + CHECK_SYS( sctp_opt_info(sk, 0, SCTP_ASSOCINFO, &assoc, &sz) ); if (sz != sizeof(assoc)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc)); @@ -142,7 +170,7 @@ static int fd_setsockopt_prebind(int sk) if (TRACE_BOOL(ANNOYING)) { /* Check new values */ - CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz) ); + CHECK_SYS( sctp_opt_info(sk, 0, SCTP_ASSOCINFO, &assoc, &sz) ); fd_log_debug( "New SCTP_ASSOCINFO : sasoc_asocmaxrxt : %hu", assoc.sasoc_asocmaxrxt); fd_log_debug( " sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations); fd_log_debug( " sasoc_peer_rwnd : %u" , assoc.sasoc_peer_rwnd); @@ -318,7 +346,7 @@ static int fd_setsockopt_prebind(int sk) sz = sizeof(parms); /* Read socket defaults */ - CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz) ); + CHECK_SYS( sctp_opt_info(sk, 0, SCTP_PEER_ADDR_PARAMS, &parms, &sz) ); if (sz != sizeof(parms)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(parms)); @@ -347,7 +375,7 @@ static int fd_setsockopt_prebind(int sk) if (TRACE_BOOL(ANNOYING)) { /* Check new values */ - CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz) ); + CHECK_SYS( sctp_opt_info(sk, 0, SCTP_PEER_ADDR_PARAMS, &parms, &sz) ); fd_log_debug( "New SCTP_PEER_ADDR_PARAMS : spp_hbinterval : %u", parms.spp_hbinterval); fd_log_debug( " spp_pathmaxrxt : %hu", parms.spp_pathmaxrxt); fd_log_debug( " spp_pathmtu : %u", parms.spp_pathmtu); @@ -814,21 +842,16 @@ redo: bind_default = 1; goto redo; } - - #if 0 - union { - sSA *sa; - uint8_t *buf; - } ptr; - int i; - ptr.sa = sar; - fd_log_debug("Calling sctp_bindx with the following address array:"); - for (i = 0; i < count; i++) { - TRACE_sSA(FD_LOG_DEBUG, FULL, " - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); - ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6) ; - } - #endif - + + /* Debug: show bound addresses */ + { + char * buf = NULL; + size_t len = 0; + CHECK_MALLOC_DO( fd_sa_dump_array( &buf, &len, 0, sar, count), ); + LOG_D("SCTP server binding local addresses: %s", buf); + free(buf); + } + /* Bind to this array */ CHECK_SYS( sctp_bindx(*sock, sar, count, SCTP_BINDX_ADD_ADDR) ); @@ -840,22 +863,19 @@ redo: CHECK_FCT( fd_setsockopt_postbind(*sock, bind_default) ); /* Debug: show all local listening addresses */ - #if 0 - sSA *sar; - union { - sSA *sa; - uint8_t *buf; - } ptr; - int sz; - + { + sSA *sar = NULL; + int sz = 0; + char * buf = NULL; + size_t len = 0; + CHECK_SYS( sz = sctp_getladdrs(*sock, 0, &sar) ); - - fd_log_debug("SCTP server bound on :"); - for (ptr.sa = sar; sz-- > 0; ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)) { - TRACE_sSA(FD_LOG_DEBUG, FULL, " - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); - } + + CHECK_MALLOC_DO( fd_sa_dump_array( &buf, &len, 0, sar, sz), ); + LOG_D("SCTP server locally bound addresses: %s", buf); sctp_freeladdrs(sar); - #endif + free(buf); + } return 0; } @@ -869,7 +889,7 @@ int fd_sctp_listen( int sock ) } /* Create a client socket and connect to remote server */ -int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list ) +int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list, struct fd_list * src_list ) { int family; union { @@ -879,11 +899,13 @@ int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list size_t size = 0; int count = 0; int ret; + int bind_default = 1; /* enable ASCONF in postbind */ sar.buf = NULL; - TRACE_ENTRY("%p %i %hu %p", sock, no_ip6, port, list); + TRACE_ENTRY("%p %i %hu %p %p", sock, no_ip6, port, list, src_list); CHECK_PARAMS( sock && list && (!FD_IS_LIST_EMPTY(list)) ); + CHECK_PARAMS( !src_list || (src_list && (!FD_IS_LIST_EMPTY(src_list))) ); if (no_ip6) { family = AF_INET; @@ -899,31 +921,47 @@ int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list /* Set the socket options */ CHECK_FCT_DO( ret = fd_setsockopt_prebind(*sock), goto out ); - + + /* Bind to explicit source addresses if requested */ + if (src_list && !FD_IS_LIST_EMPTY(src_list)) { + sSA * bindsar = NULL; /* array of addresses */ + size_t sz = 0; /* size of the array */ + int sarcount = 0; /* number of sock addr in the array */ + + /* Create the array of configured addresses */ + CHECK_FCT_DO( ret = add_addresses_from_list_mask((void *)&bindsar, &sz, &sarcount, family, 0, src_list, EP_FL_CONF, EP_FL_CONF), goto out ); + + if (sarcount) { + char * buf = NULL; + size_t len = 0; + CHECK_MALLOC_DO( fd_sa_dump_array( &buf, &len, 0, bindsar, sarcount), goto out ); + LOG_A("SCTP client binding local addresses: %s", buf); + free(buf); + + CHECK_SYS_DO( ret = sctp_bindx(*sock, bindsar, sarcount, SCTP_BINDX_ADD_ADDR), goto out ); + } + + /* Disable ASCONF option in postbind */ + bind_default = 0; + + /* We don't need bindsar anymore */ + free(bindsar); + } + /* Create the array of addresses, add first the configured addresses, then the discovered, then the other ones */ CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF, EP_FL_CONF ), goto out ); CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, EP_FL_DISC ), goto out ); CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, 0 ), goto out ); /* Try connecting */ - LOG_A("Attempting SCTP connection (%d addresses attempted) ", count); - -#if 0 - /* Dump the SAs */ - union { - uint8_t *buf; - sSA *sa; - sSA4 *sin; - sSA6 *sin6; - } ptr; - int i; - ptr.buf = sar.buf; - for (i=0; i< count; i++) { - TRACE_sSA(FD_LOG_DEBUG, FULL, " - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); - ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6); - } -#endif - + { + char * buf = NULL; + size_t len = 0; + CHECK_MALLOC_DO( fd_sa_dump_array( &buf, &len, 0, sar.sa, count), goto out ); + LOG_A("SCTP client connecting to addresses: %s", buf); + free(buf); + } + /* Bug in some Linux kernel, the sctp_connectx is not a cancellation point. To avoid blocking freeDiameter, we allow async cancel here */ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); #ifdef SCTP_CONNECTX_4_ARGS @@ -944,7 +982,7 @@ int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list free(sar.buf); sar.buf = NULL; /* Set the remaining sockopts */ - CHECK_FCT_DO( ret = fd_setsockopt_postbind(*sock, 1), + CHECK_FCT_DO( ret = fd_setsockopt_postbind(*sock, bind_default), { CHECK_SYS_DO( shutdown(*sock, SHUT_RDWR), /* continue */ ); } ); @@ -982,7 +1020,7 @@ int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary ) } #if 0 char sa_buf[sSA_DUMP_STRLEN]; - fd_sa_sdump_numeric(sa_buf, &status.sstat_primary.spinfo_address); + fd_sa_sdump_numeric(sa_buf, (sSA *)&status.sstat_primary.spinfo_address); fd_log_debug( "SCTP_STATUS : sstat_state : %i" , status.sstat_state); fd_log_debug( " sstat_rwnd : %u" , status.sstat_rwnd); fd_log_debug( " sstat_unackdata : %hu", status.sstat_unackdata); @@ -1218,6 +1256,7 @@ again: /* Handle the case where the data received is a notification */ if (mhdr.msg_flags & MSG_NOTIFICATION) { union sctp_notification * notif = (union sctp_notification *) data; + char sa_buf[sSA_DUMP_STRLEN]; TRACE_DEBUG(FULL, "Received %zdb data of notification on socket %d", datasize, conn->cc_socket); @@ -1235,7 +1274,8 @@ again: case SCTP_PEER_ADDR_CHANGE: TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification"); - /* TRACE_sSA(FD_LOG_DEBUG, ANNOYING, " intf_change : ", &(notif->sn_paddr_change.spc_aaddr), NI_NUMERICHOST | NI_NUMERICSERV, "" ); */ + fd_sa_sdump_numeric(sa_buf, (sSA *)&(notif->sn_paddr_change.spc_aaddr)); + TRACE_DEBUG(ANNOYING, " intf_change : %s", sa_buf); TRACE_DEBUG(ANNOYING, " state : %d", notif->sn_paddr_change.spc_state); TRACE_DEBUG(ANNOYING, " error : %d", notif->sn_paddr_change.spc_error); @@ -1271,10 +1311,12 @@ again: break; #ifndef OLD_SCTP_SOCKET_API +#ifdef SCTP_NOTIFICATIONS_STOPPED_EVENT case SCTP_NOTIFICATIONS_STOPPED_EVENT: TRACE_DEBUG(INFO, "Received SCTP_NOTIFICATIONS_STOPPED_EVENT notification, marking the association in error state"); *event = FDEVP_CNX_ERROR; break; +#endif /* SCTP_NOTIFICATIONS_STOPPED_EVENT */ #endif /* OLD_SCTP_SOCKET_API */ default: diff --git a/libfdcore/sctp3436.c b/libfdcore/sctp3436.c index c7c7d04..28a244b 100644 --- a/libfdcore/sctp3436.c +++ b/libfdcore/sctp3436.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -137,7 +137,7 @@ fatal: static void * decipher(void * arg) { struct sctp3436_ctx * ctx = arg; - struct cnxctx *cnx; + struct cnxctx *cnx = NULL; TRACE_ENTRY("%p", arg); CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error); |
