Mercurial > hg > freeDiameter
changeset 38:68c1890f7049
Fixed a small bug in SCTP close
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Thu, 05 Nov 2009 17:29:12 +0900 |
parents | cc3c59fe98fe |
children | d7535cf7bab5 |
files | freeDiameter/cnxctx.c freeDiameter/endpoints.c freeDiameter/fD.h freeDiameter/p_cnx.c freeDiameter/p_psm.c freeDiameter/sctp.c freeDiameter/tests/testcnx.c include/freeDiameter/freeDiameter.h |
diffstat | 8 files changed, 268 insertions(+), 64 deletions(-) [+] |
line wrap: on
line diff
--- a/freeDiameter/cnxctx.c Thu Nov 05 14:28:46 2009 +0900 +++ b/freeDiameter/cnxctx.c Thu Nov 05 17:29:12 2009 +0900 @@ -1143,6 +1143,8 @@ /* 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 */
--- a/freeDiameter/endpoints.c Thu Nov 05 14:28:46 2009 +0900 +++ b/freeDiameter/endpoints.c Thu Nov 05 17:29:12 2009 +0900 @@ -93,6 +93,27 @@ return 0; } +/* Keep only endpoints of the same family as af */ +int fd_ep_filter_family( struct fd_list * list, int af ) +{ + struct fd_list * li; + + TRACE_ENTRY("%p %d", list, af); + CHECK_PARAMS(list); + + for (li = list->next; li != list; li = li->next) { + struct fd_endpoint * ep = (struct fd_endpoint *)li; + + if (ep->sa.sa_family != af) { + li = li->prev; + fd_list_unlink(&ep->chain); + free(ep); + } + } + + return 0; +} + /* Reset the given flag(s) from all items in the list */ int fd_ep_clearflags( struct fd_list * list, uint32_t flags ) {
--- a/freeDiameter/fD.h Thu Nov 05 14:28:46 2009 +0900 +++ b/freeDiameter/fD.h Thu Nov 05 17:29:12 2009 +0900 @@ -147,6 +147,7 @@ struct cnxctx * p_initiator; /* Connection before CEA is received */ struct cnxctx * p_receiver; /* Only used in case of election */ pthread_t p_ini_thr; + struct fd_list p_connparams; /* The list of connection attempts, see p_cnx.c */ }; @@ -243,13 +244,17 @@ void fd_psm_abord(struct fd_peer * peer ); void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay); int fd_psm_change_state(struct fd_peer * peer, int new_state); -void fd_psm_cleanup(struct fd_peer * peer); +void fd_psm_cleanup(struct fd_peer * peer, int terminate); /* Peer out */ int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer); int fd_out_start(struct fd_peer * peer); int fd_out_stop(struct fd_peer * peer); +/* Initiating connections */ +int fd_p_cnx_init(struct fd_peer * peer); +void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all); + /* Peer sent requests cache */ int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc); int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req);
--- a/freeDiameter/p_cnx.c Thu Nov 05 14:28:46 2009 +0900 +++ b/freeDiameter/p_cnx.c Thu Nov 05 17:29:12 2009 +0900 @@ -37,36 +37,140 @@ /* This file contains code used by a peer state machine to initiate a connection to remote peer */ +struct next_conn { + struct fd_list chain; + int proto; /* Protocol of the next attempt */ + sSS ss; /* The address, only for TCP */ + uint16_t port; /* The port, for SCTP (included in ss for TCP) */ + int dotls; /* Handshake TLS after connection ? */ +}; + +static int prepare_connection_list(struct fd_peer * peer) +{ + /* Resolve peer address(es) if needed */ + if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) { + struct addrinfo hints, *ai, *aip; + int ret; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai); + if (ret) { + fd_log_debug("Unable to resolve address for peer '%s' (%s), aborting this connection\n", peer->p_hdr.info.pi_diamid, gai_strerror(ret)); + fd_psm_terminate( peer ); + return 0; + } + + for (aip = ai; aip != NULL; aip = aip->ai_next) { + CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC ) ); + } + freeaddrinfo(ai); + } + + /* Remove addresses from unwanted family */ + if (peer->p_hdr.info.config.pic_flags.pro3) { + CHECK_FCT( fd_ep_filter_family( + &peer->p_hdr.info.pi_endpoints, + (peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ? + AF_INET + : AF_INET6)); + } + + TODO("Prepare the list in peer->p_connparams obeying the flags"); + + TODO("Return an error if the list is empty in the end"); + + return ENOTSUP; +} + +static __inline__ void failed_connection_attempt(struct fd_peer * peer) +{ + /* Simply remove the first item in the list */ + struct fd_list * li = peer->p_connparams.next; + fd_list_unlink(li); + free(li); +} + +static void empty_connection_list(struct fd_peer * peer) +{ + /* Remove all items */ + while (!FD_IS_LIST_EMPTY(&peer->p_connparams)) { + failed_connection_attempt(peer); + } +} + /* The thread that attempts the connection */ static void * connect_thr(void * arg) { struct fd_peer * peer = arg; struct cnxctx * cnx = NULL; - - + struct next_conn * nc = NULL; - /* Use the flags in the peer to select the protocol */ - - TODO("loop on fd_cnx_cli_connect_tcp or fd_cnx_cli_connect_sctp"); - + TRACE_ENTRY("%p", arg); + CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL ); + + do { + /* Rebuild the list if needed, if it is empty */ + if (FD_IS_LIST_EMPTY(&peer->p_connparams)) { + CHECK_FCT_DO( prepare_connection_list(peer), goto fatal_error ); + if (FD_IS_LIST_EMPTY(&peer->p_connparams)) + /* Already logged and peer terminated */ + return NULL; + } + + /* Attempt connection to the first entry */ + nc = (struct next_conn *)(peer->p_connparams.next); + + switch (nc->proto) { + case IPPROTO_TCP: + cnx = fd_cnx_cli_connect_tcp((sSA *)&nc->ss, sSSlen(&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) ?: fd_g_config->cnf_flags.no_ip6, nc->port, &peer->p_hdr.info.pi_endpoints); + break; +#endif /* DISABLE_SCTP */ + } + + if (cnx) + break; + + /* Pop these parameters and continue */ + failed_connection_attempt(peer); + + pthread_testcancel(); + + } while (!cnx); /* and until cancellation */ /* Now, we have an established connection in cnx */ pthread_cleanup_push((void *)fd_cnx_destroy, cnx); /* Handshake if needed (secure port) */ - - + if (nc->dotls) { + CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL), + { + /* Handshake failed ... */ + fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid); + fd_cnx_destroy(cnx); + empty_connection_list(peer); + fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF); + return NULL; + } ); + } /* Upon success, generate FDEVP_CNX_ESTABLISHED */ CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx), goto fatal_error ); + pthread_cleanup_pop(0); return NULL; + fatal_error: /* Cleanup the connection */ - fd_cnx_destroy(cnx); + if (cnx) + fd_cnx_destroy(cnx); /* Generate a termination event */ CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); @@ -78,7 +182,25 @@ /* Initiate a connection attempt to a remote peer */ int fd_p_cnx_init(struct fd_peer * peer) { + TRACE_ENTRY("%p", peer); + /* Start the connect thread */ CHECK_FCT( pthread_create(&peer->p_ini_thr, NULL, connect_thr, peer) ); return 0; } + +/* Cancel a connection attempt */ +void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all) +{ + TRACE_ENTRY("%p %d", peer, cleanup_all); + CHECK_PARAMS_DO( CHECK_PEER(peer), return ); + + if (peer->p_ini_thr != (pthread_t)NULL) { + CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */); + failed_connection_attempt(peer); + } + + if (cleanup_all) { + empty_connection_list(peer); + } +}
--- a/freeDiameter/p_psm.c Thu Nov 05 14:28:46 2009 +0900 +++ b/freeDiameter/p_psm.c Thu Nov 05 17:29:12 2009 +0900 @@ -242,26 +242,33 @@ } /* Cleanup the peer */ -void fd_psm_cleanup(struct fd_peer * peer) +void fd_psm_cleanup(struct fd_peer * peer, int terminate) { /* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */ CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ ); - /* Destroy data */ - CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */); + + fd_p_cnx_abort(peer, terminate); + if (peer->p_cnxctx) { fd_cnx_destroy(peer->p_cnxctx); peer->p_cnxctx = NULL; } + if (peer->p_initiator) { fd_cnx_destroy(peer->p_initiator); peer->p_initiator = NULL; } + if (peer->p_receiver) { fd_cnx_destroy(peer->p_receiver); peer->p_receiver = NULL; } + if (terminate) { + CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ ); + } + } @@ -526,7 +533,7 @@ case STATE_CLOSING: /* Cleanup the peer */ - fd_psm_cleanup(peer); + fd_psm_cleanup(peer, 0); /* Reset the timer */ fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc); @@ -601,7 +608,7 @@ case STATE_WAITCNXACK: case STATE_WAITCEA: /* Destroy the connection, restart the timer to a new connection attempt */ - fd_psm_cleanup(peer); + fd_psm_cleanup(peer, 0); fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc); break; @@ -621,8 +628,7 @@ goto psm_loop; psm_end: - fd_psm_cleanup(peer); - CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ ); + fd_psm_cleanup(peer, 1); pthread_cleanup_pop(1); /* set STATE_ZOMBIE */ peer->p_psm = (pthread_t)NULL; pthread_detach(pthread_self()); @@ -674,7 +680,7 @@ CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ ); /* Cleanup the data */ - fd_psm_cleanup(peer); + fd_psm_cleanup(peer, 1); /* Destroy the event list */ CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
--- a/freeDiameter/sctp.c Thu Nov 05 14:28:46 2009 +0900 +++ b/freeDiameter/sctp.c Thu Nov 05 17:29:12 2009 +0900 @@ -640,23 +640,71 @@ return 0; } +/* Add addresses from the list that match the flags to the array */ +static int add_addresses_from_list_mask(uint8_t ** array, int * count, size_t * offset, uint16_t port, struct fd_list * list, uint32_t mask, uint32_t val) +{ + size_t sz; + struct fd_list * li; + union { + uint8_t *buf; + sSA4 *sin; + sSA6 *sin6; + } ptr; + + for (li = list->next; li != list; li = li->next) { + struct fd_endpoint * ep = (struct fd_endpoint *) li; + + /* Do the flag match ? */ + if ((val & mask) != (ep->flags & mask)) + continue; + + /* We add this endpoint at the end of array */ + (*count)++; + + /* Size of the new SA we are adding (array may contain a mix of sockaddr_in and sockaddr_in6) */ +#ifndef SCTP_USE_MAPPED_ADDRESSES + if (ep->sa.sa_family == AF_INET6) +#else /* SCTP_USE_MAPPED_ADDRESSES */ + if (family == AF_INET6) +#endif /* SCTP_USE_MAPPED_ADDRESSES */ + sz = sizeof(sSA6); + else + sz = sizeof(sSA4); + + /* augment array to contain the additional info */ + CHECK_MALLOC( *array = realloc(*array, (*offset) + sz) ); + + ptr.buf = *array + *offset; /* place of the new SA */ + (*offset) += sz; /* update to end of sar */ + + if (sz == sizeof(sSA4)) { + memcpy(ptr.buf, &ep->sin, sz); + ptr.sin->sin_port = port; + } else { + if (ep->sa.sa_family == AF_INET) { /* We must map the address */ + memset(ptr.buf, 0, sz); + ptr.sin6->sin6_family = AF_INET6; + IN6_ADDR_V4MAP( &ptr.sin6->sin6_addr.s6_addr, ep->sin.sin_addr.s_addr ); + } else { + memcpy(ptr.sin6, &ep->sin6, sz); + } + ptr.sin6->sin6_port = port; + } + } + + return 0; +} + /* 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 family; int count = 0; - size_t offset = 0, sz; + size_t offset = 0; union { uint8_t *buf; sSA *sa; } sar; - union { - uint8_t *buf; - sSA *sa; - sSA4 *sin; - sSA6 *sin6; - } ptr; - struct fd_list * li; int ret; sar.buf = NULL; @@ -679,42 +727,10 @@ /* Set the socket options */ CHECK_FCT_DO( ret = fd_setsockopt_prebind(*sock), goto fail ); - /* Create the array of addresses for sctp_connectx */ - for (li = list->next; li != list; li = li->next) { - struct fd_endpoint * ep = (struct fd_endpoint *) li; - - count++; - - /* Size of the new SA we are adding (sar may contain a mix of sockaddr_in and sockaddr_in6) */ -#ifndef SCTP_USE_MAPPED_ADDRESSES - if (ep->sa.sa_family == AF_INET6) -#else /* SCTP_USE_MAPPED_ADDRESSES */ - if (family == AF_INET6) -#endif /* SCTP_USE_MAPPED_ADDRESSES */ - sz = sizeof(sSA6); - else - sz = sizeof(sSA4); - - /* augment sar to contain the additional info */ - CHECK_MALLOC_DO( sar.buf = realloc(sar.buf, offset + sz), { ret = ENOMEM; goto fail; } ); - - ptr.buf = sar.buf + offset; /* place of the new SA */ - offset += sz; /* update to end of sar */ - - if (sz == sizeof(sSA4)) { - memcpy(ptr.buf, &ep->sin, sz); - ptr.sin->sin_port = htons(port); - } else { - if (ep->sa.sa_family == AF_INET) { /* We must map the address */ - memset(ptr.buf, 0, sz); - ptr.sin6->sin6_family = AF_INET6; - IN6_ADDR_V4MAP( &ptr.sin6->sin6_addr.s6_addr, ep->sin.sin_addr.s_addr ); - } else { - memcpy(ptr.sin6, &ep->sin6, sz); - } - ptr.sin6->sin6_port = htons(port); - } - } + /* 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, &count, &offset, htons(port), list, EP_FL_CONF, EP_FL_CONF ), goto fail ); + CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &count, &offset, htons(port), list, EP_FL_CONF | EP_FL_DISC, EP_FL_DISC ), goto fail ); + CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &count, &offset, htons(port), list, EP_FL_CONF | EP_FL_DISC, 0 ), goto fail ); /* Try connecting */ TRACE_DEBUG(FULL, "Attempting SCTP connection (%d addresses attempted)...", count);
--- a/freeDiameter/tests/testcnx.c Thu Nov 05 14:28:46 2009 +0900 +++ b/freeDiameter/tests/testcnx.c Thu Nov 05 17:29:12 2009 +0900 @@ -470,6 +470,7 @@ struct connect_flags { int proto; + int expect_failure; /* 0 or 1 */ }; void * connect_thr(void * arg) @@ -485,14 +486,14 @@ { struct fd_endpoint * ep = (struct fd_endpoint *)(eps.next); cnx = fd_cnx_cli_connect_tcp( &ep->sa, sSSlen(&ep->ss) ); - CHECK( 1, cnx ? 1 : 0 ); + CHECK( 1, (cnx ? 1 : 0) ^ cf->expect_failure ); } break; #ifndef DISABLE_SCTP case IPPROTO_SCTP: { cnx = fd_cnx_cli_connect_sctp(0, TEST_PORT, &eps); - CHECK( 1, cnx ? 1 : 0 ); + CHECK( 1, (cnx ? 1 : 0) ^ cf->expect_failure ); } break; #endif /* DISABLE_SCTP */ @@ -1509,6 +1510,36 @@ #endif /* DISABLE_SCTP */ } + /* Check that connection attempt fails then */ + { + struct connect_flags cf; + + memset(&cf, 0, sizeof(cf)); + cf.proto = IPPROTO_TCP; + cf.expect_failure = 1; + + /* Start the client thread, that should fail */ + CHECK( 0, pthread_create(&thr, 0, connect_thr, &cf) ); + CHECK( 0, pthread_join( thr, (void *)&client_side ) ); + CHECK( 0, client_side ? 1 : 0 ); + } + +#ifndef DISABLE_SCTP + { + struct connect_flags cf; + + memset(&cf, 0, sizeof(cf)); + cf.proto = IPPROTO_SCTP; + cf.expect_failure = 1; + + /* Start the client thread, that should fail */ + CHECK( 0, pthread_create(&thr, 0, connect_thr, &cf) ); + CHECK( 0, pthread_join( thr, (void *)&client_side ) ); + CHECK( 0, client_side ? 1 : 0 ); + } +#endif /* DISABLE_SCTP */ + + /* That's all for the tests yet */ PASSTEST(); }
--- a/include/freeDiameter/freeDiameter.h Thu Nov 05 14:28:46 2009 +0900 +++ b/include/freeDiameter/freeDiameter.h Thu Nov 05 17:29:12 2009 +0900 @@ -539,6 +539,7 @@ int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags ); int fd_ep_filter( struct fd_list * list, uint32_t flags ); +int fd_ep_filter_family( struct fd_list * list, int af ); int fd_ep_clearflags( struct fd_list * list, uint32_t flags ); void fd_ep_dump_one( char * prefix, struct fd_endpoint * ep, char * suffix ); void fd_ep_dump( int indent, struct fd_list * eps );