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 );
"Welcome to our mercurial repository"