changeset 24:bd83ce9328ed

Cleanups and completed sctp code (not finished)
author Sebastien Decugis <sdecugis@nict.go.jp>
date Wed, 21 Oct 2009 18:42:45 +0900
parents db6c40b8b307
children 67ca08d5bc48
files doc/freediameter.conf.sample freeDiameter/CMakeLists.txt freeDiameter/cnxctx.c freeDiameter/config.c freeDiameter/endpoints.c freeDiameter/fD.h freeDiameter/fdd.y freeDiameter/sctp.c freeDiameter/server.c freeDiameter/tcp.c include/freeDiameter/CMakeLists.txt include/freeDiameter/freeDiameter-host.h.in include/freeDiameter/freeDiameter.h include/freeDiameter/libfreeDiameter.h
diffstat 14 files changed, 633 insertions(+), 182 deletions(-) [+]
line wrap: on
line diff
--- a/doc/freediameter.conf.sample	Tue Oct 20 17:30:20 2009 +0900
+++ b/doc/freediameter.conf.sample	Wed Oct 21 18:42:45 2009 +0900
@@ -204,8 +204,8 @@
 SCTP_streams = 50;
 TcTimer = 60;
 TwTimer = 6;
-ListenOn = "133.243.146.201";
-ListenOn = "fe80::21d:9ff:fe89:7d68%eth0";
+#ListenOn = "133.243.146.201";
+#ListenOn = "fe80::21d:9ff:fe89:7d68%eth0";
 NoRelay;
 LoadExtension = "extensions/dbg_monitor.fdx";
 LoadExtension = "extensions/dict_nasreq.fdx";
--- a/freeDiameter/CMakeLists.txt	Tue Oct 20 17:30:20 2009 +0900
+++ b/freeDiameter/CMakeLists.txt	Wed Oct 21 18:42:45 2009 +0900
@@ -12,6 +12,7 @@
 	config.c
 	cnxctx.c
 	dispatch.c
+	endpoints.c
 	extensions.c
 	dict_base_proto.c
 	messages.c
--- a/freeDiameter/cnxctx.c	Tue Oct 20 17:30:20 2009 +0900
+++ b/freeDiameter/cnxctx.c	Wed Oct 21 18:42:45 2009 +0900
@@ -173,9 +173,15 @@
 	fd_cnx_destroy(cnx);
 	return NULL;
 }
-#ifndef DISABLE_SCTP
+
+/* Same function for SCTP, with a list of local endpoints to bind to */
 struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list)
 {
+#ifdef DISABLE_SCTP
+	TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
+	ASSERT(0);
+	CHECK_FCT_DO( ENOTSUP, return NULL);
+#else /* DISABLE_SCTP */
 	struct cnxctx * cnx = NULL;
 	sSS dummy;
 	sSA * sa = (sSA *) &dummy;
@@ -200,8 +206,8 @@
 error:
 	fd_cnx_destroy(cnx);
 	return NULL;
+#endif /* DISABLE_SCTP */
 }
-#endif /* DISABLE_SCTP */
 
 /* Allow clients to connect on the server socket */
 int fd_cnx_serv_listen(struct cnxctx * conn)
@@ -238,12 +244,13 @@
 	TRACE_ENTRY("%p", serv);
 	CHECK_PARAMS_DO(serv, return NULL);
 	
+	/* Accept the new connection -- this is blocking until new client enters or cancellation */
 	CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL );
 	
 	if (TRACE_BOOL(INFO)) {
-		fd_log_debug("%s - new client [", fd_cnx_getid(serv));
-		sSA_DUMP_NODE( &ss, AI_NUMERICHOST );
-		fd_log_debug("] connected.\n");
+		fd_log_debug("%s : accepted new client [", fd_cnx_getid(serv));
+		sSA_DUMP_NODE( &ss, NI_NUMERICHOST );
+		fd_log_debug("].\n");
 	}
 	
 	CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); return NULL; } );
@@ -258,25 +265,27 @@
 		
 		/* Numeric values for debug */
 		rc = getnameinfo((sSA *)&ss, sizeof(sSS), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
-		if (rc)
+		if (rc) {
 			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+			portbuf[0] = '\0';
+		}
 		
-		snprintf(cli->cc_id, sizeof(cli->cc_id), "Client %s [%s]:%s (%d) / serv (%d)", 
+		snprintf(cli->cc_id, sizeof(cli->cc_id), "Incoming %s [%s]:%s (%d) @ serv (%d)", 
 				IPPROTO_NAME(cli->cc_proto), 
 				addrbuf, portbuf, 
 				cli->cc_socket, serv->cc_socket);
 		
-		/* Textual value for log messages */
-		rc = getnameinfo((sSA *)&ss, sizeof(sSS), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, NI_NUMERICHOST);
+		/* Name for log messages */
+		rc = getnameinfo((sSA *)&ss, sizeof(sSS), 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));
 	}
 
+#ifndef DISABLE_SCTP
 	/* SCTP-specific handlings */
-#ifndef DISABLE_SCTP
 	if (cli->cc_proto == IPPROTO_SCTP) {
 		/* Retrieve the number of streams */
-		CHECK_FCT_DO( fd_sctp_get_str_info( cli->cc_socket, &cli->cc_sctp_para.str_in, &cli->cc_sctp_para.str_out ), goto error );
+		CHECK_FCT_DO( fd_sctp_get_str_info( cli->cc_socket, &cli->cc_sctp_para.str_in, &cli->cc_sctp_para.str_out, NULL ), goto error );
 		if (cli->cc_sctp_para.str_out > cli->cc_sctp_para.str_in)
 			cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_out;
 		else
@@ -290,12 +299,121 @@
 	return NULL;
 }
 
-/* Client side: connect to a remote server */
-struct cnxctx * fd_cnx_cli_connect(int proto, const sSA * sa,  socklen_t addrlen)
+/* Client side: connect to a remote server -- cancelable */
+struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, socklen_t addrlen)
+{
+	int sock;
+	struct cnxctx * cnx = NULL;
+	
+	TRACE_ENTRY("%p %d", sa, addrlen);
+	CHECK_PARAMS_DO( sa && addrlen, return NULL );
+	
+	/* Create the socket and connect, which can take some time and/or fail */
+	CHECK_FCT_DO( fd_tcp_client( &sock, sa, addrlen ), return NULL );
+	
+	if (TRACE_BOOL(INFO)) {
+		fd_log_debug("Connection established to server '");
+		sSA_DUMP_NODE_SERV( sa, NI_NUMERICSERV);
+		fd_log_debug("' (TCP:%d).\n", sock);
+	}
+	
+	/* Once the socket is created successfuly, prepare the remaining of the cnx */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); return NULL; } );
+	
+	cnx->cc_socket = sock;
+	cnx->cc_proto  = IPPROTO_TCP;
+	
+	/* Generate the names for the object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		char portbuf[10];
+		int  rc;
+		
+		/* Numeric values for debug */
+		rc = getnameinfo(sa, addrlen, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+		if (rc) {
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+			portbuf[0] = '\0';
+		}
+		
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Client of TCP server [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
+		
+		/* 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));
+	}
+	
+	return cnx;
+
+error:
+	fd_cnx_destroy(cnx);
+	return NULL;
+}
+
+/* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx) */
+struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list)
 {
+#ifdef DISABLE_SCTP
+	TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
+	ASSERT(0);
+	CHECK_FCT_DO( ENOTSUP, return NULL);
+#else /* DISABLE_SCTP */
+	int sock;
+	struct cnxctx * cnx = NULL;
+	sSS primary;
+	
+	TRACE_ENTRY("%p", list);
+	CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL );
+	
+	CHECK_FCT_DO( fd_sctp_client( &sock, no_ip6, port, list ), 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); return NULL; } );
+	
+	cnx->cc_socket = sock;
+	cnx->cc_proto  = IPPROTO_SCTP;
+	
+	/* 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;
+	
+	/* Generate the names for the object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		char portbuf[10];
+		int  rc;
+		
+		/* Numeric values for debug */
+		rc = getnameinfo((sSA *)&primary, sizeof(sSS), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+		if (rc) {
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+			portbuf[0] = '\0';
+		}
+		
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Client of SCTP server [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
+		
+		/* Name for log messages */
+		rc = getnameinfo((sSA *)&primary, sizeof(sSS), 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));
+	}
+	
+	if (TRACE_BOOL(INFO)) {
+		fd_log_debug("Connection established to server '");
+		sSA_DUMP_NODE_SERV( &primary, NI_NUMERICSERV);
+		fd_log_debug("' (SCTP:%d).\n", sock);
+	}
+	
+	return cnx;
 
-	TODO("...");
+error:
+	fd_cnx_destroy(cnx);
 	return NULL;
+#endif /* DISABLE_SCTP */
 }
 
 /* Return a string describing the connection, for debug */
@@ -414,7 +532,7 @@
 				sSS ss;
 				socklen_t sl;
 				CHECK_FCT(fd_tcp_get_local_ep(conn->cc_socket, &ss, &sl));
-				CHECK_FCT(fd_ep_add_merge( local, (sSA *)&ss, sl, 0, 0, 0, 1 ));
+				CHECK_FCT(fd_ep_add_merge( local, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY));
 			}
 			break;
 
@@ -440,7 +558,7 @@
 				sSS ss;
 				socklen_t sl;
 				CHECK_FCT(fd_tcp_get_remote_ep(conn->cc_socket, &ss, &sl));
-				CHECK_FCT(fd_ep_add_merge( remote, (sSA *)&ss, sl, 0, 0, 0, 1 ));
+				CHECK_FCT(fd_ep_add_merge( remote, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY ));
 			}
 			break;
 
@@ -527,7 +645,3 @@
 	/* Done! */
 	return;
 }
-
-
-
-
--- a/freeDiameter/config.c	Tue Oct 20 17:30:20 2009 +0900
+++ b/freeDiameter/config.c	Wed Oct 21 18:42:45 2009 +0900
@@ -271,43 +271,3 @@
 	
 	return 0;
 }
-
-/* Add an endpoint information in a list */
-int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, int conf, int disc, int adv, int ll )
-{
-	struct fd_endpoint * ep;
-	struct fd_list * li;
-	int cmp = -1;
-	
-	TRACE_ENTRY("%p %p %u %i %i %i %i", list, sa, sl, conf, disc, adv, ll);
-	CHECK_PARAMS( list && sa && (sl <= sizeof(sSS)) && (conf || disc || adv || ll) );
-	
-	/* Search place in the list */
-	for (li = list->next; li != list; li = li->next) {
-		ep = (struct fd_endpoint *)li;
-		
-		cmp = memcmp(&ep->ss, sa, sl);
-		if (cmp >= 0)
-			break;
-	}
-	
-	if (cmp) {
-		/* new item to be added */
-		CHECK_MALLOC( ep = malloc(sizeof(struct fd_endpoint)) );
-		memset(ep, 0, sizeof(struct fd_endpoint));
-		fd_list_init(&ep->chain, NULL);
-		memcpy(&ep->ss, sa, sl);
-		
-		/* Insert in the list */
-		fd_list_insert_before(li, &ep->chain);
-	}
-	
-	/* Merge the flags */
-	ep->meta.conf = conf || ep->meta.conf;
-	ep->meta.disc = disc || ep->meta.disc;
-	ep->meta.adv  =  adv || ep->meta.adv;
-	ep->meta.ll   =   ll || ep->meta.ll;
-	
-	return 0;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/freeDiameter/endpoints.c	Wed Oct 21 18:42:45 2009 +0900
@@ -0,0 +1,110 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fD.h"
+
+
+/* Add an endpoint information in a list */
+int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags )
+{
+	struct fd_endpoint * ep;
+	struct fd_list * li;
+	int cmp = -1;
+	
+	TRACE_ENTRY("%p %p %u %x", list, sa, sl, flags);
+	CHECK_PARAMS( list && sa && (sl <= sizeof(sSS)) );
+	
+	/* Search place in the list */
+	for (li = list->next; li != list; li = li->next) {
+		ep = (struct fd_endpoint *)li;
+		
+		cmp = memcmp(&ep->ss, sa, sl);
+		if (cmp >= 0)
+			break;
+	}
+	
+	if (cmp) {
+		/* new item to be added */
+		CHECK_MALLOC( ep = malloc(sizeof(struct fd_endpoint)) );
+		memset(ep, 0, sizeof(struct fd_endpoint));
+		fd_list_init(&ep->chain, NULL);
+		memcpy(&ep->ss, sa, sl);
+		
+		/* Insert in the list */
+		fd_list_insert_before(li, &ep->chain);
+	}
+	
+	/* Merge the flags */
+	ep->flags |= flags;
+	
+	return 0;
+}
+
+/* Delete endpoints that do not have a matching flag from a list (0: delete all endpoints) */
+int fd_ep_filter( struct fd_list * list, uint32_t flags )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %x", list, flags);
+	CHECK_PARAMS(list);
+	
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		
+		if (! (ep->flags & flags)) {
+			li = li->prev;
+			fd_list_unlink(&ep->chain);
+			free(ep);
+		}
+	}
+	
+	return 0;
+}
+
+/* Reset the given flag(s) from all items in the list */
+int fd_ep_clearflags( struct fd_list * list, uint32_t flags )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %x", list, flags);
+	CHECK_PARAMS(list);
+	
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		ep->flags &= ~flags;
+	}
+	
+	return 0;
+}
--- a/freeDiameter/fD.h	Tue Oct 20 17:30:20 2009 +0900
+++ b/freeDiameter/fD.h	Wed Oct 21 18:42:45 2009 +0900
@@ -206,7 +206,8 @@
 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(int proto, const sSA * sa,  socklen_t addrlen);
+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);
 char * fd_cnx_getid(struct cnxctx * conn);
 int fd_cnx_start_clear(struct cnxctx * conn);
 int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority);
@@ -221,6 +222,7 @@
 /* TCP */
 int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
 int fd_tcp_listen( int sock );
+int fd_tcp_client( int *sock, sSA * sa, socklen_t salen );
 int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl);
 int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl);
 
@@ -228,9 +230,10 @@
 #ifndef DISABLE_SCTP
 int fd_sctp_create_bind_server( int * sock, struct fd_list * list, uint16_t port );
 int fd_sctp_listen( int sock );
+int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list );
 int fd_sctp_get_local_ep(int sock, struct fd_list * list);
 int fd_sctp_get_remote_ep(int sock, struct fd_list * list);
-int fd_sctp_get_str_info( int socket, int *in, int *out );
+int fd_sctp_get_str_info( int sock, int *in, int *out, sSS *primary );
 
 #endif /* DISABLE_SCTP */
 
--- a/freeDiameter/fdd.y	Tue Oct 20 17:30:20 2009 +0900
+++ b/freeDiameter/fdd.y	Wed Oct 21 18:42:45 2009 +0900
@@ -218,7 +218,7 @@
 				hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
 				ret = getaddrinfo($3, NULL, &hints, &ai);
 				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
-				CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, 1, 0, 0, 0 ), YYERROR );
+				CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF ), YYERROR );
 				freeaddrinfo(ai);
 				free($3);
 			}
@@ -429,13 +429,13 @@
 				ret = getaddrinfo($4, NULL, &hints, &ai);
 				if (ret == EAI_NONAME) {
 					/* The name was maybe not numeric, try again */
-					disc = 1;
+					disc = EP_FL_DISC;
 					hints.ai_flags &= ~ AI_NUMERICHOST;
 					ret = getaddrinfo($4, NULL, &hints, &ai);
 				}
 				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
 				
-				CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, 1, disc, 0, 0 ), YYERROR );
+				CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF | disc ), YYERROR );
 				free($4);
 				freeaddrinfo(ai);
 			}
--- a/freeDiameter/sctp.c	Tue Oct 20 17:30:20 2009 +0900
+++ b/freeDiameter/sctp.c	Wed Oct 21 18:42:45 2009 +0900
@@ -75,15 +75,15 @@
 			return ENOTSUP;
 		}
 		
-		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_data_io_event          : %hhu", event.sctp_data_io_event);
-		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_association_event      : %hhu", event.sctp_association_event);
-		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_address_event          : %hhu", event.sctp_address_event);
-		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_send_failure_event     : %hhu", event.sctp_send_failure_event);
-		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_peer_error_event       : %hhu", event.sctp_peer_error_event);
-		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_shutdown_event         : %hhu", event.sctp_shutdown_event);
-		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_partial_delivery_event : %hhu", event.sctp_partial_delivery_event);
-		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event);
-		// TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_authentication_event   : %hhu", event.sctp_authentication_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS : sctp_data_io_event          : %hhu", event.sctp_data_io_event);
+		TRACE_DEBUG(FULL, "       	 sctp_association_event      : %hhu", event.sctp_association_event);
+		TRACE_DEBUG(FULL, "       	 sctp_address_event	     : %hhu", event.sctp_address_event);
+		TRACE_DEBUG(FULL, "       	 sctp_send_failure_event     : %hhu", event.sctp_send_failure_event);
+		TRACE_DEBUG(FULL, "       	 sctp_peer_error_event       : %hhu", event.sctp_peer_error_event);
+		TRACE_DEBUG(FULL, "       	 sctp_shutdown_event	     : %hhu", event.sctp_shutdown_event);
+		TRACE_DEBUG(FULL, "       	 sctp_partial_delivery_event : %hhu", event.sctp_partial_delivery_event);
+		TRACE_DEBUG(FULL, "       	 sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event);
+		// TRACE_DEBUG(FULL, "             sctp_authentication_event    : %hhu", event.sctp_authentication_event);
 		#endif /* DEBUG_SCTP */
 		
 	}
@@ -103,10 +103,10 @@
 			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init));
 			return ENOTSUP;
 		}
-		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
-		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_instreams  : %hu", init.sinit_max_instreams);
-		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_attempts   : %hu", init.sinit_max_attempts);
-		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG : sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
+		TRACE_DEBUG(FULL, "                   sinit_max_instreams  : %hu", init.sinit_max_instreams);
+		TRACE_DEBUG(FULL, "                   sinit_max_attempts   : %hu", init.sinit_max_attempts);
+		TRACE_DEBUG(FULL, "                   sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
 		#endif /* DEBUG_SCTP */
 
 		/* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters */
@@ -119,10 +119,10 @@
 		#ifdef DEBUG_SCTP
 		/* Check new values */
 		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
-		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
-		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_instreams  : %hu", init.sinit_max_instreams);
-		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_attempts   : %hu", init.sinit_max_attempts);
-		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG : sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
+		TRACE_DEBUG(FULL, "                   sinit_max_instreams  : %hu", init.sinit_max_instreams);
+		TRACE_DEBUG(FULL, "                   sinit_max_attempts   : %hu", init.sinit_max_attempts);
+		TRACE_DEBUG(FULL, "                   sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
 		#endif /* DEBUG_SCTP */
 	}
 	
@@ -174,9 +174,9 @@
 			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo));
 			return ENOTSUP;
 		}
-		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_initial : %u", rtoinfo.srto_initial);
-		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_max     : %u", rtoinfo.srto_max);
-		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_min     : %u", rtoinfo.srto_min);
+		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO : srto_initial : %u", rtoinfo.srto_initial);
+		TRACE_DEBUG(FULL, "                   srto_max     : %u", rtoinfo.srto_max);
+		TRACE_DEBUG(FULL, "                   srto_min     : %u", rtoinfo.srto_min);
 		#endif /* DEBUG_SCTP */
 
 		rtoinfo.srto_max     = fd_g_config->cnf_timer_tw * 500 - 1000;	/* Maximum retransmit timer (in ms) (set to Tw / 2 - 1) */
@@ -187,9 +187,9 @@
 		#ifdef DEBUG_SCTP
 		/* Check new values */
 		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
-		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_initial : %u", rtoinfo.srto_initial);
-		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_max     : %u", rtoinfo.srto_max);
-		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_min     : %u", rtoinfo.srto_min);
+		TRACE_DEBUG(FULL, "New SCTP_RTOINFO : srto_initial : %u", rtoinfo.srto_initial);
+		TRACE_DEBUG(FULL, "                   srto_max     : %u", rtoinfo.srto_max);
+		TRACE_DEBUG(FULL, "                   srto_min     : %u", rtoinfo.srto_min);
 		#endif /* DEBUG_SCTP */
 	}
 	#else /* SCTP_RTOINFO */
@@ -213,11 +213,11 @@
 			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc));
 			return ENOTSUP;
 		}
-		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
-		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
-		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
-		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
-		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
+		TRACE_DEBUG(FULL, "                     sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
+		TRACE_DEBUG(FULL, "                     sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
+		TRACE_DEBUG(FULL, "                     sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
+		TRACE_DEBUG(FULL, "                     sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
 		#endif /* DEBUG_SCTP */
 
 		assoc.sasoc_asocmaxrxt = 5;	/* Maximum retransmission attempts: we want fast detection of errors */
@@ -228,11 +228,11 @@
 		#ifdef DEBUG_SCTP
 		/* Check new values */
 		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
-		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
-		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
-		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
-		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
-		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
+		TRACE_DEBUG(FULL, "                     sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
+		TRACE_DEBUG(FULL, "                     sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
+		TRACE_DEBUG(FULL, "                     sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
+		TRACE_DEBUG(FULL, "                     sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
 		#endif /* DEBUG_SCTP */
 	}
 	#else /* SCTP_ASSOCINFO */
@@ -257,8 +257,8 @@
 			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger));
 			return ENOTSUP;
 		}
-		TRACE_DEBUG(FULL, "Def SO_LINGER l_onoff  : %d", linger.l_onoff);
-		TRACE_DEBUG(FULL, "Def SO_LINGER l_linger : %d", linger.l_linger);
+		TRACE_DEBUG(FULL, "Def SO_LINGER : l_onoff  : %d", linger.l_onoff);
+		TRACE_DEBUG(FULL, "                l_linger : %d", linger.l_linger);
 		#endif /* DEBUG_SCTP */
 		
 		linger.l_onoff	= 0;	/* Do not activate the linger */
@@ -270,8 +270,8 @@
 		#ifdef DEBUG_SCTP
 		/* Check new values */
 		CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
-		TRACE_DEBUG(FULL, "New SO_LINGER l_onoff  : %d", linger.l_onoff);
-		TRACE_DEBUG(FULL, "New SO_LINGER l_linger : %d", linger.l_linger);
+		TRACE_DEBUG(FULL, "New SO_LINGER : l_onoff  : %d", linger.l_onoff);
+		TRACE_DEBUG(FULL, "                l_linger : %d", linger.l_linger);
 		#endif /* DEBUG_SCTP */
 	}
 	#else /* SO_LINGER */
@@ -369,8 +369,11 @@
 		TRACE_DEBUG(FULL, "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false");
 		#endif /* DEBUG_SCTP */
 
+#ifndef SCTP_USE_MAPPED_ADDRESSES
 		v4mapped = 0;	/* We don't want v4 mapped addresses */
-		v4mapped = 1;	/* but we have to, otherwise the bind fails in linux currently ... (Ok, It'd be better with a cmake test, any volunteer?) */
+#else /* SCTP_USE_MAPPED_ADDRESSES */
+		v4mapped = 1;	/* but we may have to, otherwise the bind fails in some environments */
+#endif /* SCTP_USE_MAPPED_ADDRESSES */
 		
 		/* Set the option to the socket */
 		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped))  );
@@ -421,7 +424,7 @@
 	   SCTP_GET_ASSOC_ID_LIST	list of these associations
 	*/
 	
-	/* In case of no_ip4, force the v6only option */
+	/* In case of no_ip4, force the v6only option -- is it a valid option for SCTP ? */
 	#ifdef IPV6_V6ONLY
 	if (fd_g_config->cnf_flags.no_ip4) {
 		int opt = 1;
@@ -525,40 +528,61 @@
 		
 	} else {
 		/* Explicit endpoints to bind to from config */
+		
 		union {
-			sSA  	*sa;
-			sSA4 	*sin;
-			sSA6 	*sin6;
-		} sar; /* build the list of endpoints to bind to */
+			sSA     * sa;
+			sSA4	*sin;
+			sSA6	*sin6;
+			uint8_t *buf;
+		} ptr;
+		union {
+			sSA     * sa;
+			uint8_t * buf;
+		} sar;
 		int count = 0; /* number of sock addr in sar array */
+		size_t offset = 0;
 		struct fd_list * li;
 		
-		sar.sa = NULL;
+		sar.buf = NULL;
 		
 		/* Create a flat array from the list of configured addresses */
 		for (li = list->next; li != list; li = li->next) {
 			struct fd_endpoint * ep = (struct fd_endpoint *)li;
+			size_t sz = 0;
 			
-			if ( ! ep->meta.conf )
+			if (! (ep->flags & EP_FL_CONF))
 				continue;
 			
 			count++;
-			if (fd_g_config->cnf_flags.no_ip6) {
-				ASSERT(ep->sa.sa_family == AF_INET);
-				CHECK_MALLOC( sar.sa = realloc(sar.sa, count * sizeof(sSA4))  );
-				memcpy(&sar.sin[count - 1], &ep->sin, sizeof(sSA4));
-				sar.sin[count - 1].sin_port = htons(port);
+			
+			/* 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( sar.buf = realloc(sar.buf, offset + sz)  );
+			
+			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 {
-				/* Pass all addresses as IPv6, eventually mapped -- we already filtered out IPv4 addresses if no_ip4 flag is set */
-				CHECK_MALLOC( sar.sa = realloc(sar.sa, count * sizeof(sSA6))  );
-				if (ep->sa.sa_family == AF_INET) {
-					memset(&sar.sin6[count - 1], 0, sizeof(sSA6));
-					sar.sin6[count - 1].sin6_family = AF_INET6;
-					IN6_ADDR_V4MAP( &sar.sin6[count - 1].sin6_addr.s6_addr, ep->sin.sin_addr.s_addr );
+				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(&sar.sin6[count - 1], &ep->sin6, sizeof(sSA6));
+					memcpy(ptr.sin6, &ep->sin6, sz);
 				}
-				sar.sin6[count - 1].sin6_port = htons(port);
+				ptr.sin6->sin6_port = htons(port);
 			}
 		}
 		
@@ -571,17 +595,20 @@
 		# ifdef DEBUG_SCTP
 		if (TRACE_BOOL(FULL)) {
 			int i;
-			fd_log_debug("Calling sctp_bindx with the following array:\n");
+			ptr.buf = sar.buf;
+			fd_log_debug("Calling sctp_bindx with the following address array:\n");
 			for (i = 0; i < count; i++) {
-				fd_log_debug("    - ");
-				sSA_DUMP_NODE_SERV( (sar.sa[0].sa_family == AF_INET) ? (sSA *)(&sar.sin[i]) : (sSA *)(&sar.sin6[i]), NI_NUMERICHOST | NI_NUMERICSERV );
-				fd_log_debug("\n");
+				TRACE_DEBUG_sSA(FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
+				ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6) ;
 			}
 		}
 		#endif /* DEBUG_SCTP */
 		
+		/* Bind to this array */
 		CHECK_SYS(  sctp_bindx(*sock, sar.sa, count, SCTP_BINDX_ADD_ADDR)  );
 		
+		/* We don't need sar anymore */
+		free(sar.buf);
 	}
 	
 	/* Now, the server is bound, set remaining sockopt */
@@ -590,16 +617,18 @@
 	#ifdef DEBUG_SCTP
 	/* Debug: show all local listening addresses */
 	if (TRACE_BOOL(FULL)) {
-		sSA *sa, *sar;
+		sSA *sar;
+		union {
+			sSA	*sa;
+			uint8_t *buf;
+		} ptr;
 		int sz;
 		
 		CHECK_SYS(  sz = sctp_getladdrs(*sock, 0, &sar)  );
 		
 		fd_log_debug("SCTP server bound on :\n");
-		for (sa = sar; sz-- > 0; sa = (sSA *)(((uint8_t *)sa) + ((sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)))) {
-			fd_log_debug("    - ");
-			sSA_DUMP_NODE_SERV( sa, NI_NUMERICHOST | NI_NUMERICSERV );
-			fd_log_debug("\n");
+		for (ptr.sa = sar; sz-- > 0; ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)) {
+			TRACE_DEBUG_sSA(FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
 		}
 		sctp_freeladdrs(sar);
 	}
@@ -616,12 +645,143 @@
 	return 0;
 }
 
-/* Retrieve streams information from a connected association */
-int fd_sctp_get_str_info( int socket, int *in, int *out )
+/* 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 )
 {
-	TODO("Retrieve streams info from the socket");
+	int family;
+	int count = 0;
+	size_t offset = 0, sz;
+	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;
+	
+	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 );
 	
-	return ENOTSUP;
+	/* 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);
+		}
+	}
+	
+	/* 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; } );
+	free(sar.buf); sar.buf = NULL;
+	
+	/* Set the remaining sockopts */
+	CHECK_FCT_DO( ret = fd_setsockopt_postbind(*sock, 1), goto fail );
+	
+	/* Done! */
+	pthread_cleanup_pop(0);
+	return 0;
+	
+fail:
+	if (*sock > 0) {
+		shutdown(*sock, SHUT_RDWR);
+		*sock = -1;
+	}
+	free(sar.buf);
+	return ret;
+}
+
+/* Retrieve streams information from a connected association -- optionaly provide the primary address */
+int fd_sctp_get_str_info( int sock, int *in, int *out, sSS *primary )
+{
+	struct sctp_status status;
+	socklen_t sz = sizeof(status);
+	
+	TRACE_ENTRY("%d %p %p %p", sock, in, out, primary);
+	CHECK_PARAMS( (sock > 0) && in && out );
+	
+	/* Read the association parameters */
+	memset(&status, 0, sizeof(status));
+	CHECK_SYS(  getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) );
+	if (sz != sizeof(status))
+	{
+		TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %g", sz, sizeof(status));
+		return ENOTSUP;
+	}
+	#ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "SCTP_STATUS : sstat_state                  : %i" , status.sstat_state);
+	TRACE_DEBUG(FULL, "              sstat_rwnd  	              : %u" , status.sstat_rwnd);
+	TRACE_DEBUG(FULL, "		 sstat_unackdata	      : %hu", status.sstat_unackdata);
+	TRACE_DEBUG(FULL, "		 sstat_penddata 	      : %hu", status.sstat_penddata);
+	TRACE_DEBUG(FULL, "		 sstat_instrms  	      : %hu", status.sstat_instrms);
+	TRACE_DEBUG(FULL, "		 sstat_outstrms 	      : %hu", status.sstat_outstrms);
+	TRACE_DEBUG(FULL, "		 sstat_fragmentation_point    : %u" , status.sstat_fragmentation_point);
+	TRACE_DEBUG_sSA(FULL, "		 sstat_primary.spinfo_address : ", &status.sstat_primary.spinfo_address, NI_NUMERICHOST | NI_NUMERICSERV, "" );
+	TRACE_DEBUG(FULL, "		 sstat_primary.spinfo_state   : %d" , status.sstat_primary.spinfo_state);
+	TRACE_DEBUG(FULL, "		 sstat_primary.spinfo_cwnd    : %u" , status.sstat_primary.spinfo_cwnd);
+	TRACE_DEBUG(FULL, "		 sstat_primary.spinfo_srtt    : %u" , status.sstat_primary.spinfo_srtt);
+	TRACE_DEBUG(FULL, "		 sstat_primary.spinfo_rto     : %u" , status.sstat_primary.spinfo_rto);
+	TRACE_DEBUG(FULL, "		 sstat_primary.spinfo_mtu     : %u" , status.sstat_primary.spinfo_mtu);
+	#endif /* DEBUG_SCTP */
+	
+	*in = (int)status.sstat_instrms;
+	*out = (int)status.sstat_outstrms;
+	
+	if (primary)
+		memcpy(primary, &status.sstat_primary.spinfo_address, sizeof(sSS));
+	
+	return 0;
 }
 
 /* Get the list of local endpoints of the socket */
@@ -629,8 +789,6 @@
 {
 	union {
 		sSA	*sa;
-		sSA4	*sin;
-		sSA6	*sin6;
 		uint8_t	*buf;
 	} ptr;
 	
@@ -645,24 +803,92 @@
 	ptr.sa = data;
 	
 	while (count) {
-		TODO("get the data from ptr");
-		TODO("Increment ptr to the next sa in data");
-		
+		socklen_t sl;
+		switch (ptr.sa->sa_family) {
+			case AF_INET:	sl = sizeof(sSA4); break;
+			case AF_INET6:	sl = sizeof(sSA6); break;
+			default:
+				TRACE_DEBUG(INFO, "Unkown address family returned in sctp_getladdrs: %d", ptr.sa->sa_family);
+		}
+				
+		CHECK_FCT( fd_ep_add_merge( list, ptr.sa, sl, EP_FL_LL ) );
+		ptr.buf += sl;
 		count --;
 	}
 	
-	/* And now, free the list and return */
+	/* Free the list */
 	sctp_freeladdrs(data);
 	
-	return ENOTSUP;
+	/* Now get the primary address, the add function will take care of merging with existing entry */
+	{
+		 
+		struct sctp_status status;
+		socklen_t sz = sizeof(status);
+		int ret;
+		
+		memset(&status, 0, sizeof(status));
+		/* Attempt to use SCTP_STATUS message to retrieve the primary address */
+		ret = getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz);
+		if (sz != sizeof(status))
+			ret = -1;
+		sz = sizeof(sSS);
+		if (ret < 0)
+		{
+			/* Fallback to getsockname -- not recommended by draft-ietf-tsvwg-sctpsocket-19#section-7.4 */
+			CHECK_SYS(getsockname(sock, (sSA *)&status.sstat_primary.spinfo_address, &sz));
+		}
+			
+		CHECK_FCT( fd_ep_add_merge( list, (sSA *)&status.sstat_primary.spinfo_address, sz, EP_FL_PRIMARY ) );
+	}
+	
+	return 0;
 }
 
 /* Get the list of remote endpoints of the socket */
 int fd_sctp_get_remote_ep(int sock, struct fd_list * list)
 {
-	TODO("SCTP: sctp_getpaddrs");
-		
+	union {
+		sSA	*sa;
+		uint8_t	*buf;
+	} ptr;
+	
+	sSA * data;
+	int count;
+	
+	TRACE_ENTRY("%d %p", sock, list);
+	CHECK_PARAMS(list);
+	
+	/* Read the list on the socket */
+	CHECK_SYS( count = sctp_getpaddrs(sock, 0, &data)  );
+	ptr.sa = data;
 	
-	return ENOTSUP;
+	while (count) {
+		socklen_t sl;
+		switch (ptr.sa->sa_family) {
+			case AF_INET:	sl = sizeof(sSA4); break;
+			case AF_INET6:	sl = sizeof(sSA6); break;
+			default:
+				TRACE_DEBUG(INFO, "Unkown address family returned in sctp_getpaddrs: %d", ptr.sa->sa_family);
+		}
+				
+		CHECK_FCT( fd_ep_add_merge( list, ptr.sa, sl, EP_FL_LL ) );
+		ptr.buf += sl;
+		count --;
+	}
+	
+	/* Free the list */
+	sctp_freepaddrs(data);
+	
+	/* Now get the primary address, the add function will take care of merging with existing entry */
+	{
+		sSS ss;
+		socklen_t sl = sizeof(sSS);
+	
+		CHECK_SYS(getpeername(sock, (sSA *)&ss, &sl));
+		CHECK_FCT( fd_ep_add_merge( list, (sSA *)&ss, sl, EP_FL_PRIMARY ) );
+	}
+	
+	/* Done! */
+	return 0;
 }
 
--- a/freeDiameter/server.c	Tue Oct 20 17:30:20 2009 +0900
+++ b/freeDiameter/server.c	Wed Oct 21 18:42:45 2009 +0900
@@ -290,7 +290,7 @@
 			for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
 				struct fd_endpoint * ep = (struct fd_endpoint *)li;
 				sSA * sa = (sSA *) &ep->ss;
-				if (! ep->meta.conf)
+				if (! (ep->flags & EP_FL_CONF))
 					continue;
 				if (fd_g_config->cnf_flags.no_ip4 && (sa->sa_family == AF_INET))
 					continue;
--- a/freeDiameter/tcp.c	Tue Oct 20 17:30:20 2009 +0900
+++ b/freeDiameter/tcp.c	Wed Oct 21 18:42:45 2009 +0900
@@ -112,6 +112,32 @@
 	return 0;
 }
 
+/* Create a client socket and connect to remote server */
+int fd_tcp_client( int *sock, sSA * sa, socklen_t salen )
+{
+	TRACE_ENTRY("%p %p %d", sock, sa, salen);
+	CHECK_PARAMS( sock && sa && salen );
+	
+	/* Create the socket */
+	CHECK_SYS(  *sock = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)  );
+	
+	/* Cleanup if we are cancelled */
+	pthread_cleanup_push(fd_cleanup_socket, sock);
+	
+	/* Set the socket options */
+	CHECK_FCT(  fd_tcp_setsockopt(sa->sa_family, *sock)  );
+	
+	TRACE_DEBUG_sSA(FULL, "Attempting TCP connection with peer: ", sa, NI_NUMERICHOST | NI_NUMERICSERV, "..." );
+	
+	/* Try connecting to the remote address */
+	CHECK_SYS( connect(*sock, sa, salen) );
+	
+	/* Done! */
+	pthread_cleanup_pop(0);
+	return 0;
+}
+
+
 /* Get the local name of a TCP socket -- would be nice if it did not return "0.0.0.0"... */
 int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl)
 {
--- a/include/freeDiameter/CMakeLists.txt	Tue Oct 20 17:30:20 2009 +0900
+++ b/include/freeDiameter/CMakeLists.txt	Wed Oct 21 18:42:45 2009 +0900
@@ -12,6 +12,7 @@
 OPTION(DISABLE_SCTP "Disable SCTP support?" OFF)
 IF (NOT DISABLE_SCTP)
 	OPTION(DEBUG_SCTP "Verbose SCTP (for debug)?" OFF)
+	OPTION(SCTP_USE_MAPPED_ADDRESSES "Use v6-mapped v4 addresses in SCTP (workaround some SCTP limitations)?" OFF)
 ENDIF (NOT DISABLE_SCTP)
 
 # Find TODO items in the code easily ?
--- a/include/freeDiameter/freeDiameter-host.h.in	Tue Oct 20 17:30:20 2009 +0900
+++ b/include/freeDiameter/freeDiameter-host.h.in	Wed Oct 21 18:42:45 2009 +0900
@@ -45,6 +45,7 @@
 
 #cmakedefine DISABLE_SCTP
 #cmakedefine DEBUG_SCTP
+#cmakedefine SCTP_USE_MAPPED_ADDRESSES
 #cmakedefine ERRORS_ON_TODO
 #cmakedefine DEBUG
 
--- a/include/freeDiameter/freeDiameter.h	Tue Oct 20 17:30:20 2009 +0900
+++ b/include/freeDiameter/freeDiameter.h	Wed Oct 21 18:42:45 2009 +0900
@@ -117,26 +117,24 @@
 /* Endpoints */
 struct fd_endpoint {
 	struct fd_list  chain;	/* link in cnf_endpoints list */
+	
 	union {
-		sSS		ss;	/* the socket information. List is always ordered by ss value (memcmp) */
+		sSS		ss;	/* the socket information. List is always ordered by ss value (memcmp) -- see fd_ep_add_merge */
 		sSA4		sin;
 		sSA6		sin6;
 		sSA		sa;
 	};
-	struct {
-		unsigned conf : 1; /* This endpoint is statically configured in a configuration file */
-		unsigned disc : 1; /* This endpoint was resolved from the Diameter Identity or other DNS query */
-		unsigned adv  : 1; /* This endpoint was advertized in Diameter CER/CEA exchange */
-		unsigned ll   : 1; /* Lower layer mechanism provided this endpoint */
+	
+#define	EP_FL_CONF	(1 << 0)	/* This endpoint is statically configured in a configuration file */
+#define	EP_FL_DISC	(1 << 1)	/* This endpoint was resolved from the Diameter Identity or other DNS query */
+#define	EP_FL_ADV	(1 << 2)	/* This endpoint was advertized in Diameter CER/CEA exchange */
+#define	EP_FL_LL	(1 << 3)	/* Lower layer mechanism provided this endpoint */
+#define	EP_FL_PRIMARY	(1 << 4)	/* This endpoint is primary in a multihomed SCTP association */
+	uint32_t	flags;		/* Additional information about the endpoint */
 		
-		/* To add: a validity timestamp for DNS records ? How do we retrieve this lifetime from DNS ? */
-
-	}		meta;	/* Additional information about the endpoint */
+	/* To add: a validity timestamp for DNS records ? How do we retrieve this lifetime from DNS ? */
 };
 
-/* Add a new entry in a list of endpoints -- merge if the sockaddr was already there */
-int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, int conf, int disc, int adv, int ll );
-
 /* Applications */
 struct fd_app {
 	struct fd_list	 chain;	/* link in cnf_apps list. List ordered by appid. */
@@ -467,4 +465,14 @@
 /* Note: if we want to support capabilities updates, we'll have to add possibility to remove an app as well... */
 
 
+/***************************************/
+/*   Endpoints lists helpers           */
+/***************************************/
+
+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_clearflags( struct fd_list * list, uint32_t flags );
+
+
+
 #endif /* _FREEDIAMETER_H */
--- a/include/freeDiameter/libfreeDiameter.h	Tue Oct 20 17:30:20 2009 +0900
+++ b/include/freeDiameter/libfreeDiameter.h	Wed Oct 21 18:42:45 2009 +0900
@@ -335,27 +335,7 @@
 		fd_log_debug("(NULL / ANY)");			\
 	}							\
 }
-/* Same, for a service */
-#define sSA_DUMP_SERV( sa, flag ) {				\
-	sSA * __sa = (sSA *)(sa);				\
-	char __servbuf[32];					\
-	if (__sa) {						\
-	  int __rc = getnameinfo(__sa, 				\
-	  		sizeof(sSS),				\
-			NULL,					\
-			0,					\
-			__servbuf,				\
-			sizeof(__servbuf),			\
-			flag);					\
-	  if (__rc)						\
-	  	fd_log_debug("%s", (char *)gai_strerror(__rc));	\
-	  else							\
-	  	fd_log_debug("%s", &__servbuf[0]);		\
-	} else {						\
-		fd_log_debug("(unknown)");			\
-	}							\
-}
-/* Combine both */
+/* Same but with the port (service) also */
 #define sSA_DUMP_NODE_SERV( sa, flag ) {				\
 	sSA * __sa = (sSA *)(sa);					\
 	char __addrbuf[INET6_ADDRSTRLEN];				\
@@ -376,6 +356,19 @@
 		fd_log_debug("(NULL / ANY)");				\
 	}								\
 }
+/* Inside a debug trace */
+#define TRACE_DEBUG_sSA(level, prefix, sa, flags, suffix ) {										\
+	if ( TRACE_BOOL(level) ) {												\
+		char __buf[25];													\
+		char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
+		fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
+			  "\t%s|%*s" prefix ,  											\
+					__thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
+					(level < FULL)?"@":" ",level, ""); 							\
+		sSA_DUMP_NODE_SERV( sa, flags );										\
+		fd_log_debug(suffix "\n");											\
+	}															\
+}
 
 
 /* A l4 protocol name (TCP / SCTP) */
@@ -480,6 +473,14 @@
 {
 	free(buffer);
 }
+static __inline__ void fd_cleanup_socket(void * sockptr)
+{
+	if (sockptr) {
+		shutdown(*(int *)sockptr, SHUT_RDWR);
+		*(int *)sockptr = 0;
+	}
+}
+
 
 /*============================================================*/
 /*                          LISTS                             */
"Welcome to our mercurial repository"