Commit cf5a5317 authored by Sebastien Decugis's avatar Sebastien Decugis
Browse files

Fixed a small bug in SCTP close

parent 61193b7f
......@@ -1143,6 +1143,8 @@ void fd_cnx_destroy(struct cnxctx * conn)
/* 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 */
......
......@@ -93,6 +93,27 @@ int fd_ep_filter( struct fd_list * list, uint32_t flags )
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 )
{
......
......@@ -147,6 +147,7 @@ struct fd_peer { /* The "real" definition of the peer structure */
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 @@ int fd_psm_terminate(struct fd_peer * peer );
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);
......
......@@ -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 ? */
};
/* The thread that attempts the connection */
static void * connect_thr(void * arg)
static int prepare_connection_list(struct fd_peer * peer)
{
struct fd_peer * peer = arg;
struct cnxctx * cnx = NULL;
/* 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");
/* Use the flags in the peer to select the protocol */
TODO("Return an error if the list is empty in the end");
TODO("loop on fd_cnx_cli_connect_tcp or fd_cnx_cli_connect_sctp");
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;
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 @@ fatal_error:
/* 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);
}
}
......@@ -242,26 +242,33 @@ void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay)
}
/* 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 @@ psm_loop:
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 @@ psm_loop:
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 @@ psm_loop:
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 @@ void fd_psm_abord(struct fd_peer * peer )
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 */ );
......
......@@ -640,52 +640,28 @@ int fd_sctp_listen( int sock )
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 )
/* 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)
{
int family;
int count = 0;
size_t offset = 0, sz;
union {
uint8_t *buf;
sSA *sa;
} sar;
size_t sz;
struct fd_list * li;
union {
uint8_t *buf;
sSA *sa;
sSA4 *sin;
sSA6 *sin6;
} ptr;
struct fd_list * li;
int ret;
sar.buf = NULL;
TRACE_ENTRY("%p %i %hu %p", sock, no_ip6, port, list);
CHECK_PARAMS( sock && list && (!FD_IS_LIST_EMPTY(list)) );
if (no_ip6) {
family = AF_INET;
} else {
family = AF_INET6;
}
/* Create the socket */
CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
/* Cleanup if we are cancelled */
pthread_cleanup_push(fd_cleanup_socket, sock);
/* 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++;
/* Do the flag match ? */
if ((val & mask) != (ep->flags & mask))
continue;
/* Size of the new SA we are adding (sar may contain a mix of sockaddr_in and sockaddr_in6) */
/* 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 */
......@@ -695,15 +671,15 @@ int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list
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; } );
/* augment array to contain the additional info */
CHECK_MALLOC( *array = realloc(*array, (*offset) + sz) );
ptr.buf = sar.buf + offset; /* place of the new SA */
offset += sz; /* update to end of sar */
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 = htons(port);
ptr.sin->sin_port = port;
} else {
if (ep->sa.sa_family == AF_INET) { /* We must map the address */
memset(ptr.buf, 0, sz);
......@@ -712,10 +688,50 @@ int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list
} else {
memcpy(ptr.sin6, &ep->sin6, sz);
}
ptr.sin6->sin6_port = htons(port);
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;
union {
uint8_t *buf;
sSA *sa;
} sar;
int ret;
sar.buf = NULL;
TRACE_ENTRY("%p %i %hu %p", sock, no_ip6, port, list);
CHECK_PARAMS( sock && list && (!FD_IS_LIST_EMPTY(list)) );
if (no_ip6) {
family = AF_INET;
} else {
family = AF_INET6;
}
/* Create the socket */
CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
/* Cleanup if we are cancelled */
pthread_cleanup_push(fd_cleanup_socket, sock);
/* Set the socket options */
CHECK_FCT_DO( ret = fd_setsockopt_prebind(*sock), goto fail );
/* 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);
CHECK_SYS_DO( sctp_connectx(*sock, sar.sa, count), { ret = errno; goto fail; } );
......
......@@ -470,6 +470,7 @@ struct fd_list eps = FD_LIST_INITIALIZER(eps);
struct connect_flags {
int proto;
int expect_failure; /* 0 or 1 */
};
void * connect_thr(void * arg)
......@@ -485,14 +486,14 @@ void * connect_thr(void * arg)
{
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 @@ int main(int argc, char *argv[])
#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();
}
......
......@@ -539,6 +539,7 @@ int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor,
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 );
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment