diff freeDiameter/sctp.c @ 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
line wrap: on
line diff
--- 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;
 }
 
"Welcome to our mercurial repository"