Przeglądaj źródła

- experimental sctp support (one to many)

Andrei Pelinescu-Onciul 17 lat temu
rodzic
commit
c3611173e7
7 zmienionych plików z 777 dodań i 80 usunięć
  1. 175 51
      main.c
  2. 66 0
      sctp_options.c
  3. 47 0
      sctp_options.h
  4. 351 0
      sctp_server.c
  5. 38 0
      sctp_server.h
  6. 73 0
      socket_info.c
  7. 27 29
      socket_info.h

+ 175 - 51
main.c

@@ -75,6 +75,7 @@
  * 2007-06-07  added support for locking pages in mem. and using real time
  *              scheduling policies (andrei)
  * 2007-07-30  dst blacklist and DNS cache measurements added (Gergo)
+ * 2008-08-08  sctp support (andrei)
  */
 
 
@@ -145,6 +146,10 @@
 #include "tls_hooks_init.h"
 #endif /* CORE_TLS */
 #endif /* USE_TCP */
+#ifdef USE_SCTP
+#include "sctp_options.h"
+#include "sctp_server.h"
+#endif
 #include "usr_avp.h"
 #include "core_cmd.h"
 #include "flags.h"
@@ -211,6 +216,10 @@ Options:\n\
     -N           Number of tcp child processes (default: equal to `-n')\n\
     -W           poll method\n"
 #endif
+#ifdef USE_SCTP
+"    -S           Disable sctp\n\
+    -O            Number of sctp child processes (default: equal to `-n')\n"
+#endif /* USE_SCTP */
 "    -V           Version number\n\
     -h           This help message\n\
     -b nr        Maximum receive buffer size which will not be exceeded by\n\
@@ -288,6 +297,10 @@ int tls_disable = 0;  /* tls enabled by default */
 int tls_disable = 1;  /* tls disabled by default */
 #endif /* CORE_TLS */
 #endif /* USE_TLS */
+#ifdef USE_SCTP
+int sctp_children_no = 0;
+int sctp_disable = 0; /* 1 if sctp is disabled */
+#endif /* USE_SCTP */
 
 struct process_table *pt=0;		/*array with children pids, 0= main proc,
 									alloc'ed in shared mem if possible*/
@@ -392,6 +405,9 @@ struct socket_info* tcp_listen=0;
 #ifdef USE_TLS
 struct socket_info* tls_listen=0;
 #endif
+#ifdef USE_SCTP
+struct socket_info* sctp_listen=0;
+#endif
 struct socket_info* bind_address=0; /* pointer to the crt. proc.
 									 listening address*/
 struct socket_info* sendipv4; /* ipv4 socket to use when msg. comes from ipv6*/
@@ -404,6 +420,10 @@ struct socket_info* sendipv6_tcp;
 struct socket_info* sendipv4_tls;
 struct socket_info* sendipv6_tls;
 #endif
+#ifdef USE_SCTP
+struct socket_info* sendipv4_sctp;
+struct socket_info* sendipv6_sctp;
+#endif
 
 unsigned short port_no=0; /* default port*/
 #ifdef USE_TLS
@@ -486,6 +506,9 @@ void cleanup(show_status)
 	destroy_tls();
 #endif /* USE_TLS */
 #endif /* USE_TCP */
+#ifdef USE_SCTP
+	destroy_sctp();
+#endif
 	destroy_timer();
 	destroy_script_cb();
 	destroy_nonsip_hooks();
@@ -777,29 +800,46 @@ error:
  * sets proto */
 static int parse_proto(unsigned char* s, long len, int* proto)
 {
-#define PROTO2UINT(a, b, c) ((	(((unsigned int)(a))<<16)+ \
+#define PROTO2UINT3(a, b, c) ((	(((unsigned int)(a))<<16)+ \
 								(((unsigned int)(b))<<8)+  \
 								((unsigned int)(c)) ) | 0x20202020)
+#define PROTO2UINT4(a, b ,c ,d) ((	(((unsigned int)(a))<<24)+ \
+									(((unsigned int)(b))<<16)+ \
+									(((unsigned int)(c))<< 8)+ \
+									(((unsigned int)(d))) \
+								  )| 0x20202020 )
 	unsigned int i;
-	if (len!=3) return -1;
-	i=PROTO2UINT(s[0], s[1], s[2]);
-	switch(i){
-		case PROTO2UINT('u', 'd', 'p'):
-			*proto=PROTO_UDP;
-			break;
+	if (likely(len==3)){
+		i=PROTO2UINT3(s[0], s[1], s[2]);
+		switch(i){
+			case PROTO2UINT3('u', 'd', 'p'):
+				*proto=PROTO_UDP;
+				break;
 #ifdef USE_TCP
-		case PROTO2UINT('t', 'c', 'p'):
-			*proto=PROTO_TCP;
-			break;
+			case PROTO2UINT3('t', 'c', 'p'):
+				*proto=PROTO_TCP;
+				break;
 #ifdef USE_TLS
-		case PROTO2UINT('t', 'l', 's'):
-			*proto=PROTO_TLS;
-			break;
+			case PROTO2UINT3('t', 'l', 's'):
+				*proto=PROTO_TLS;
+				break;
 #endif
 #endif
-		default:
+			default:
+				return -1;
+		}
+	}
+#ifdef USE_SCTP
+	else if (likely(len==4)){
+		i=PROTO2UINT4(s[0], s[1], s[2], s[3]);
+		if (i==PROTO2UINT4('s', 'c', 't', 'p'))
+			*proto=PROTO_SCTP;
+		else
 			return -1;
 	}
+#endif /* USE_SCTP */
+	else
+		return -1;
 	return 0;
 }
 
@@ -1032,12 +1072,13 @@ int main_loop()
 			"stand-alone receiver @ %s:%s",
 			 bind_address->name.s, bind_address->port_no_str.s );
 
-	/* call it also w/ PROC_MAIN to make sure modules that init things only
-	 * in PROC_MAIN get a chance to run */
-	if (init_child(PROC_MAIN) < 0) {
-		LOG(L_ERR, "ERROR: main_dontfork: init_child(PROC_MAIN) -- exiting\n");
-		goto error;
-	}
+		/* call it also w/ PROC_MAIN to make sure modules that init things 
+		 * only in PROC_MAIN get a chance to run */
+		if (init_child(PROC_MAIN) < 0) {
+			LOG(L_ERR, "ERROR: main_dontfork: init_child(PROC_MAIN) "
+						"-- exiting\n");
+			goto error;
+		}
 
 		/* We will call child_init even if we
 		 * do not fork - and it will be called with rank 1 because
@@ -1064,6 +1105,22 @@ int main_loop()
 				sendipv6=si;
 	#endif
 		}
+#ifdef USE_SCTP
+		if (!sctp_disable){
+			for(si=sctp_listen; si; si=si->next){
+				if (sctp_init_sock(si)==-1)  goto error;
+				/* get first ipv4/ipv6 socket*/
+				if ((si->address.af==AF_INET)&&
+						((sendipv4_sctp==0)||
+						 	(sendipv4_sctp->flags&(SI_IS_LO|SI_IS_MCAST))))
+					sendipv4_sctp=si;
+		#ifdef USE_IPV6
+				if((sendipv6_sctp==0)&&(si->address.af==AF_INET6))
+					sendipv6_sctp=si;
+		#endif
+			}
+		}
+#endif /* USE_SCTP */
 #ifdef USE_TCP
 		if (!tcp_disable){
 			for(si=tcp_listen; si; si=si->next){
@@ -1099,8 +1156,8 @@ int main_loop()
 #endif /* USE_TLS */
 #endif /* USE_TCP */
 
-			/* all processes should have access to all the sockets (for sending)
-			 * so we open all first*/
+			/* all processes should have access to all the sockets (for 
+			 * sending) so we open all first*/
 		if (do_suid()==-1) goto error; /* try to drop privileges */
 
 		/* init childs with rank==PROC_INIT before forking any process,
@@ -1139,12 +1196,37 @@ int main_loop()
 			/*parent*/
 			/*close(udp_sock)*/; /*if it's closed=>sendto invalid fd errors?*/
 		}
-	}
+#ifdef USE_SCTP
+		/* sctp processes */
+		if (!sctp_disable){
+			for(si=sctp_listen; si; si=si->next){
+				for(i=0;i<sctp_children_no;i++){
+					snprintf(si_desc, MAX_PT_DESC, "sctp receiver child=%d "
+								"sock=%s:%s",
+								i, si->name.s, si->port_no_str.s);
+					child_rank++;
+					pid = fork_process(child_rank, si_desc, 1);
+					if (pid<0){
+						LOG(L_CRIT,  "main_loop: Cannot fork\n");
+						goto error;
+					}else if (pid==0){
+						/* child */
+						bind_address=si; /* shortcut */
+#ifdef STATS
+						setstats( i+r*children_no );
+#endif
+						return sctp_rcv_loop();
+					}
+				}
+			/*parent*/
+			/*close(sctp_sock)*/; /*if closed=>sendto invalid fd errors?*/
+			}
+		}
+#endif /* USE_SCTP */
 
-	/*this is the main process*/
-	bind_address=0;				/* main proc -> it shouldn't send anything, */
+		/*this is the main process*/
+		bind_address=0;	/* main proc -> it shouldn't send anything, */
 
-	{
 #ifdef USE_SLOW_TIMER
 		/* fork again for the "slow" timer process*/
 		pid = fork_process(PROC_TIMER, "slow timer", 1);
@@ -1173,17 +1255,15 @@ int main_loop()
 				set_rt_prio(rt_timer1_prio, rt_timer1_policy);
 			if (arm_timer()<0) goto error;
 			timer_main();
-		}else{
 		}
-	}
 
-/* init childs with rank==MAIN before starting tcp main (in case they want to
- *  fork  a tcp capable process, the corresponding tcp. comm. fds in pt[] must
- *  be set before calling tcp_main_loop()) */
-	if (init_child(PROC_MAIN) < 0) {
-		LOG(L_ERR, "ERROR: main: error in init_child\n");
-		goto error;
-	}
+	/* init childs with rank==MAIN before starting tcp main (in case they want
+	 * to fork  a tcp capable process, the corresponding tcp. comm. fds in
+	 * pt[] must be set before calling tcp_main_loop()) */
+		if (init_child(PROC_MAIN) < 0) {
+			LOG(L_ERR, "ERROR: main: error in init_child\n");
+			goto error;
+		}
 
 #ifdef USE_TCP
 		if (!tcp_disable){
@@ -1204,32 +1284,32 @@ int main_loop()
 			}
 		}
 #endif
-	/* main */
-	strncpy(pt[0].desc, "attendant", MAX_PT_DESC );
+		/* main */
+		strncpy(pt[0].desc, "attendant", MAX_PT_DESC );
 #ifdef USE_TCP
-	close_extra_socks(PROC_ATTENDANT, get_proc_no());
-	if(!tcp_disable){
-		/* main's tcp sockets are disabled by default from init_pt() */
-		unix_tcp_sock=-1;
-	}
+		close_extra_socks(PROC_ATTENDANT, get_proc_no());
+		if(!tcp_disable){
+			/* main's tcp sockets are disabled by default from init_pt() */
+			unix_tcp_sock=-1;
+		}
 #endif
 
-	/*DEBUG- remove it*/
 #ifdef EXTRA_DEBUG
-	for (r=0; r<*process_count; r++){
-		fprintf(stderr, "% 3d   % 5d - %s\n", r, pt[r].pid, pt[r].desc);
-	}
+		for (r=0; r<*process_count; r++){
+			fprintf(stderr, "% 3d   % 5d - %s\n", r, pt[r].pid, pt[r].desc);
+		}
 #endif
-	DBG("Expect maximum %d  open fds\n", get_max_open_fds());
+		DBG("Expect maximum %d  open fds\n", get_max_open_fds());
 
-	for(;;){
+		for(;;){
 			handle_sigs();
 			pause();
+		}
+	
 	}
 
-
 	/*return 0; */
- error:
+error:
 				 /* if we are here, we are the "main process",
 				  any forked children should exit with exit(-1) and not
 				  ever use return */
@@ -1245,8 +1325,14 @@ static int calc_proc_no(void)
 {
 	int udp_listeners;
 	struct socket_info* si;
+#ifdef USE_SCTP
+	int sctp_listeners;
+#endif
 
 	for (si=udp_listen, udp_listeners=0; si; si=si->next, udp_listeners++);
+#ifdef USE_SCTP
+	for (si=sctp_listen, sctp_listeners=0; si; si=si->next, sctp_listeners++);
+#endif
 	return
 		     /* receivers and attendant */
 		(dont_fork ? 1 : children_no * udp_listeners + 1)
@@ -1258,6 +1344,9 @@ static int calc_proc_no(void)
 #endif
 #ifdef USE_TCP
 		+((!tcp_disable)?( 1/* tcp main */ + tcp_children_no ):0)
+#endif
+#ifdef USE_SCTP
+		+((!sctp_disable)?sctp_children_no*sctp_listeners:0)
 #endif
 		;
 }
@@ -1295,7 +1384,7 @@ int main(int argc, char** argv)
 		"DBG_MSG_QA enabled, ser may exit abruptly\n");
 #endif
 
-	options=  ":f:cm:dVhEb:l:L:n:vrRDTN:W:w:t:u:g:P:G:"
+	options=  ":f:cm:dVhEb:l:L:n:vrRDTN:W:w:t:u:g:P:G:SO:"
 #ifdef STATS
 		"s:"
 #endif
@@ -1303,6 +1392,9 @@ int main(int argc, char** argv)
 	
 #ifdef USE_TCP
 	init_tcp_options(); /* set the defaults before the config */
+#endif
+#ifdef USE_SCTP
+	init_sctp_options(); /* set defaults before the config */
 #endif
 	/* look if there is a -h, e.g. -f -h construction won't catch it later */
 	opterr = 0;
@@ -1535,6 +1627,25 @@ try_again:
 					fprintf(stderr,"WARNING: tcp support not compiled in\n");
 				#endif
 					break;
+			case 'S':
+				#ifdef USE_SCTP
+					sctp_disable=1;
+				#else
+					fprintf(stderr,"WARNING: sctp support not compiled in\n");
+				#endif
+					break;
+			case 'O':
+				#ifdef USE_SCTP
+					sctp_children_no=strtol(optarg, &tmp, 10);
+					if ((tmp==0) ||(*tmp)){
+						fprintf(stderr, "bad process number: -O %s\n",
+									optarg);
+						goto error;
+					}
+				#else
+					fprintf(stderr,"WARNING: sctp support not compiled in\n");
+				#endif
+					break;
 			case 'w':
 					working_dir=optarg;
 					break;
@@ -1573,6 +1684,14 @@ try_again:
 	/* init locks first */
 	if (init_lock_ops()!=0)
 		goto error;
+#ifdef USE_TCP
+#ifdef USE_TLS
+	if (tcp_disable)
+		tls_disable=1; /* if no tcp => no tls */
+#endif /* USE_TLS */
+#endif /* USE_TCP */
+	/* initialize the configured proto list */
+	init_proto_order();
 	/* init the resolver, before fixing the config */
 	resolv_init();
 	/* fix parameters */
@@ -1588,6 +1707,11 @@ try_again:
 		if (tcp_children_no<=0) tcp_children_no=children_no;
 	}
 #endif
+#ifdef USE_SCTP
+	if (!sctp_disable){
+		if (sctp_children_no<=0) sctp_children_no=children_no;
+	}
+#endif
 
 	if (working_dir==0) working_dir="/";
 

+ 66 - 0
sctp_options.c

@@ -0,0 +1,66 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* 
+ * sctp options
+ */
+/*
+ * History:
+ * --------
+ *  2008-08-07  initial version (andrei)
+ */
+
+
+#include "sctp_options.h"
+#include "dprint.h"
+
+
+struct sctp_cfg_options sctp_options;
+
+void init_sctp_options()
+{
+#ifdef USE_SCTP
+	sctp_options.sctp_autoclose=DEFAULT_SCTP_AUTOCLOSE; /* in seconds */
+	sctp_options.sctp_send_ttl=DEFAULT_SCTP_SEND_TTL;   /* in milliseconds */
+#endif
+}
+
+
+
+#define W_OPT_NSCTP(option) \
+	if (sctp_options.option){\
+		WARN("sctp_options: " #option \
+			" cannot be enabled (sctp support not compiled-in)\n"); \
+			sctp_options.option=0; \
+	}
+
+
+
+void sctp_options_check()
+{
+#ifndef USE_SCTP
+	W_OPT_NSCTP(sctp_autoclose);
+	W_OPT_NSCTP(sctp_send_ttl);
+#endif
+}
+
+
+
+void sctp_options_get(struct sctp_cfg_options *s)
+{
+	*s=sctp_options;
+}

+ 47 - 0
sctp_options.h

@@ -0,0 +1,47 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* 
+ * sctp options
+ */
+/*
+ * History:
+ * --------
+ *  2008-08-07  initial version (andrei)
+ */
+
+#ifndef _sctp_options_h
+#define _sctp_options_h
+
+#define DEFAULT_SCTP_AUTOCLOSE 180 /* seconds */
+#define DEFAULT_SCTP_SEND_TTL  32000 /* in ms (32s)  */
+
+
+struct sctp_cfg_options{
+	int sctp_so_rcvbuf;
+	int sctp_so_sndbuf;
+	unsigned int sctp_autoclose; /* in seconds */
+	unsigned int sctp_send_ttl; /* in milliseconds */
+};
+
+extern struct sctp_cfg_options sctp_options;
+
+void init_sctp_options();
+void sctp_options_check();
+void sctp_options_get(struct sctp_cfg_options *s);
+
+#endif /* _sctp_options_h */

+ 351 - 0
sctp_server.c

@@ -0,0 +1,351 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* 
+ * sctp one to many 
+ */
+/*
+ * History:
+ * --------
+ *  2008-08-07  initial version (andrei)
+ */
+
+#ifdef USE_SCTP
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/sctp.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+#include "sctp_server.h"
+#include "sctp_options.h"
+#include "globals.h"
+#include "config.h"
+#include "dprint.h"
+#include "receive.h"
+#include "mem/mem.h"
+#include "ip_addr.h"
+#include "cfg/cfg_struct.h"
+
+
+
+int sctp_init_sock(struct socket_info* sock_info)
+{
+	union sockaddr_union* addr;
+	int optval;
+	socklen_t optlen;
+	
+	addr=&sock_info->su;
+	sock_info->proto=PROTO_SCTP;
+	if (init_su(addr, &sock_info->address, sock_info->port_no)<0){
+		LOG(L_ERR, "ERROR: sctp_init_sock: could not init sockaddr_union\n");
+		goto error;
+	}
+	sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_SEQPACKET, 
+								IPPROTO_SCTP);
+	if (sock_info->socket==-1){
+		LOG(L_ERR, "ERROR: sctp_init_sock: socket: %s\n", strerror(errno));
+		goto error;
+	}
+	INFO("sctp: socket %d initialized (%p)\n", sock_info->socket, sock_info);
+	/* make socket non-blocking */
+#if 0
+	/* MSG_WAITALL doesn't work for recvmsg, so use blocking sockets
+	 * and send with MSG_DONTWAIT */
+	optval=fcntl(sock_info->socket, F_GETFL);
+	if (optval==-1){
+		LOG(L_ERR, "ERROR: init_sctp: fnctl failed: (%d) %s\n",
+				errno, strerror(errno));
+		goto error;
+	}
+	if (fcntl(sock_info->socket, F_SETFL, optval|O_NONBLOCK)==-1){
+		LOG(L_ERR, "ERROR: init_sctp: fcntl: set non-blocking failed:"
+				" (%d) %s\n", errno, strerror(errno));
+		goto error;
+	}
+#endif
+
+	/* set sock opts */
+	/* set receive buffer: SO_RCVBUF*/
+	if (sctp_options.sctp_so_rcvbuf){
+		optval=sctp_options.sctp_so_rcvbuf;
+		if (setsockopt(sock_info->socket, SOL_SCTP, SO_RCVBUF,
+					(void*)&optval, sizeof(optval)) ==-1){
+			LOG(L_ERR, "ERROR: sctp_init_sock: setsockopt: SO_RCVBUF (%d):"
+						" %s\n", optval, strerror(errno));
+			/* continue, non-critical */
+		}
+	}
+	
+	/* set send buffer: SO_SNDBUF */
+	if (sctp_options.sctp_so_sndbuf){
+		optval=sctp_options.sctp_so_sndbuf;
+		if (setsockopt(sock_info->socket, SOL_SCTP, SO_SNDBUF,
+					(void*)&optval, sizeof(optval)) ==-1){
+			LOG(L_ERR, "ERROR: sctp_init_sock: setsockopt: SO_SNDBUF (%d):"
+						" %s\n", optval, strerror(errno));
+			/* continue, non-critical */
+		}
+	}
+	
+	/* disable fragments interleave (SCTP_FRAGMENT_INTERLEAVE) --
+	 * we don't want partial delivery, so fragment interleave must be off too
+	 */
+	optval=0;
+	if (setsockopt(sock_info->socket, SOL_SCTP, SCTP_FRAGMENT_INTERLEAVE ,
+					(void*)&optval, sizeof(optval)) ==-1){
+		LOG(L_ERR, "ERROR: sctp_init_sock: setsockopt: %s\n",
+						strerror(errno));
+		goto error;
+	}
+	
+	/* turn off partial delivery: on linux setting SCTP_PARTIAL_DELIVERY_POINT
+	 * to 0 or a very large number seems to be enough, however the portable
+	 * way to do it is to set it to the socket receive buffer size
+	 * (this is the maximum value allowed in the sctp api draft) */
+	optlen=sizeof(optval);
+	if (getsockopt(sock_info->socket, SOL_SCTP, SO_RCVBUF,
+					(void*)&optval, &optlen) ==-1){
+		LOG(L_ERR, "ERROR: sctp_init_sock: getsockopt: %s\n",
+						strerror(errno));
+		goto error;
+	}
+	if (setsockopt(sock_info->socket, SOL_SCTP, SCTP_PARTIAL_DELIVERY_POINT,
+					(void*)&optval, sizeof(optval)) ==-1){
+		LOG(L_ERR, "ERROR: sctp_init_sock: setsockopt: %s\n",
+						strerror(errno));
+		goto error;
+	}
+	
+	/* nagle / no delay */
+	optval=1;
+	if (setsockopt(sock_info->socket, SOL_SCTP, SCTP_NODELAY,
+					(void*)&optval, sizeof(optval)) ==-1){
+		LOG(L_ERR, "ERROR: sctp_init_sock: setsockopt: %s\n",
+						strerror(errno));
+		/* non critical, try to continue */
+	}
+	
+	/* enable message fragmentation (SCTP_DISABLE_FRAGMENTS)  (on send) */
+	optval=0;
+	if (setsockopt(sock_info->socket, SOL_SCTP, SCTP_DISABLE_FRAGMENTS,
+					(void*)&optval, sizeof(optval)) ==-1){
+		LOG(L_ERR, "ERROR: sctp_init_sock: setsockopt: %s\n",
+						strerror(errno));
+		/* non critical, try to continue */
+	}
+	
+	/* set autoclose */
+	optval=sctp_options.sctp_autoclose;
+	if (setsockopt(sock_info->socket, SOL_SCTP, SCTP_DISABLE_FRAGMENTS,
+					(void*)&optval, sizeof(optval)) ==-1){
+		LOG(L_ERR, "ERROR: sctp_init_sock: setsockopt: %s\n",
+						strerror(errno));
+		/* non critical, try to continue */
+	}
+	
+	/* SCTP_EVENTS for SCTP_SNDRCV (sctp_data_io_event) -> per message
+	 *  information in sctp_sndrcvinfo */
+	
+	/* SCTP_EVENTS for send dried out -> present in the draft not yet
+	 * present in linux (might help to detect when we could send again to
+	 * some peer, kind of poor's man poll on write, based on received
+	 * SCTP_SENDER_DRY_EVENTs */
+	
+	
+	/* bind the addresses  (TODO multiple addresses support)*/
+	if (bind(sock_info->socket,  &addr->s, sockaddru_len(*addr))==-1){
+		LOG(L_ERR, "ERROR: sctp_init_sock: bind(%x, %p, %d) on %s: %s\n",
+				sock_info->socket, &addr->s, 
+				(unsigned)sockaddru_len(*addr),
+				sock_info->address_str.s,
+				strerror(errno));
+	#ifdef USE_IPV6
+		if (addr->s.sa_family==AF_INET6)
+			LOG(L_ERR, "ERROR: sctp_init_sock: might be caused by using a "
+							"link local address, try site local or global\n");
+	#endif
+		goto error;
+	}
+	if (listen(sock_info->socket, 1)<0){
+		LOG(L_ERR, "ERROR: sctp_init_sock: listen(%x, 1) on %s: %s\n",
+					sock_info->socket, sock_info->address_str.s,
+					strerror(errno));
+		goto error;
+	}
+	return 0;
+error:
+	return -1;
+}
+
+
+
+int sctp_rcv_loop()
+{
+	unsigned len;
+	static char buf [BUF_SIZE+1];
+	char *tmp;
+	struct receive_info ri;
+	struct sctp_sndrcvinfo* sinfo;
+	struct msghdr msg;
+	struct iovec iov[1];
+	/*struct cmsghdr* cmsg; */
+	char cbuf[CMSG_SPACE(sizeof(*sinfo))];
+
+	
+	ri.bind_address=bind_address; /* this will not change */
+	ri.dst_port=bind_address->port_no;
+	ri.dst_ip=bind_address->address;
+	ri.proto=PROTO_SCTP;
+	ri.proto_reserved1=ri.proto_reserved2=0;
+	
+	iov[0].iov_base=buf;
+	iov[0].iov_len=BUF_SIZE;
+	msg.msg_iov=iov;
+	msg.msg_iovlen=1;
+	msg.msg_control=cbuf;
+	msg.msg_controllen=sizeof(cbuf);
+	msg.msg_flags=0;
+	
+
+	/* initialize the config framework */
+	if (cfg_child_init()) goto error;
+	
+	for(;;){
+		/* recv
+		 * recvmsg must be used because the socket is non-blocking
+		 * and we want MSG_WAITALL */
+		msg.msg_name=&ri.src_su.s;
+		msg.msg_namelen=sockaddru_len(bind_address->su);
+
+		len=recvmsg(bind_address->socket, &msg, MSG_WAITALL);
+		/* len=sctp_recvmsg(bind_address->socket, buf, BUF_SIZE, &ri.src_su.s,
+							&msg.msg_namelen, &sinfo, &msg.msg_flags); */
+		if (len==-1){
+			if (errno==EAGAIN){
+				DBG("sctp_rcv_loop: EAGAIN on sctp socket\n");
+				continue;
+			}
+			LOG(L_ERR, "ERROR: sctp_rcv_loop: sctp_recvmsg on %d (%p):"
+						"[%d] %s\n", bind_address->socket, bind_address,
+						errno, strerror(errno));
+			if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED))
+				continue; /* goto skip;*/
+			else goto error;
+		}
+		if (unlikely(msg.msg_flags & MSG_NOTIFICATION)){
+			/* intercept usefull notifications: TODO */
+			DBG("sctp_rcv_loop: MSG_NOTIFICATION\n");
+			/* notification in CMSG data */
+			continue;
+		}else if (unlikely(!(msg.msg_flags & MSG_EOR))){
+			LOG(L_ERR, "ERROR: sctp_rcv_loop: partial delivery not"
+						"supported\n");
+			continue;
+		}
+		/* we  0-term the messages for debugging */
+		buf[len]=0; /* no need to save the previous char */
+		su2ip_addr(&ri.src_ip, &ri.src_su);
+		ri.src_port=su_getport(&ri.src_su);
+
+		/* sanity checks */
+		if (len<MIN_SCTP_PACKET) {
+			tmp=ip_addr2a(&ri.src_ip);
+			DBG("sctp_rcv_loop: probing packet received from %s %d\n",
+					tmp, htons(ri.src_port));
+			continue;
+		}
+		if (ri.src_port==0){
+			tmp=ip_addr2a(&ri.src_ip);
+			LOG(L_INFO, "sctp_rcv_loop: dropping 0 port packet from %s\n",
+						tmp);
+			continue;
+		}
+	
+		/* update the local config */
+		cfg_update();
+		receive_msg(buf, len, &ri);
+	}
+error:
+	return -1;
+}
+
+
+/* send buf:len over udp to dst (uses only the to and send_sock dst members)
+ * returns the numbers of bytes sent on success (>=0) and -1 on error
+ */
+int sctp_msg_send(struct dest_info* dst, char* buf, unsigned len)
+{
+	int n;
+	int tolen;
+	struct ip_addr ip; /* used only on error, for debugging */
+	struct msghdr msg;
+	struct iovec iov[1];
+	
+	tolen=sockaddru_len(dst->to);
+	iov[0].iov_base=buf;
+	iov[0].iov_len=len;
+	msg.msg_iov=iov;
+	msg.msg_iovlen=1;
+	msg.msg_name=&dst->to.s;
+	msg.msg_namelen=tolen;
+	msg.msg_control=0;
+	msg.msg_controllen=0;
+	msg.msg_flags=SCTP_UNORDERED;
+again:
+	n=sendmsg(dst->send_sock->socket, &msg, MSG_DONTWAIT);
+#if 0
+	n=sctp_sendmsg(dst->send_sock->socket, buf, len, &dst->to.s, tolen,
+					0 /* ppid */, SCTP_UNORDERED /* | SCTP_EOR */ /* flags */,
+					0 /* stream */, sctp_options.sctp_send_ttl /* ttl */,
+					0 /* context */);
+#endif
+	if (n==-1){
+		su2ip_addr(&ip, &dst->to);
+		LOG(L_ERR, "ERROR: sctp_msg_send: sendmsg(sock,%p,%d,0,%s:%d,%d):"
+				" %s(%d)\n", buf, len, ip_addr2a(&ip), su_getport(&dst->to),
+				tolen, strerror(errno),errno);
+		if (errno==EINTR) goto again;
+		if (errno==EINVAL) {
+			LOG(L_CRIT,"CRITICAL: invalid sendmsg parameters\n"
+			"one possible reason is the server is bound to localhost and\n"
+			"attempts to send to the net\n");
+		}else if (errno==EAGAIN || errno==EWOULDBLOCK){
+			LOG(L_ERR, "ERROR: sctp_msg_send: failed to send, send buffers"
+						" full\n");
+			/* TODO: fix blocking writes */
+		}
+	}
+	return n;
+}
+
+
+
+void destroy_sctp()
+{
+}
+
+#endif /* USE_SCTP */

+ 38 - 0
sctp_server.h

@@ -0,0 +1,38 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* 
+ * sctp one to many 
+ */
+/*
+ * History:
+ * --------
+ *  2008-08-07  initial version (andrei)
+ */
+
+#ifndef _sctp_server_h
+#define _sctp_server_h
+
+#include "ip_addr.h"
+
+int sctp_init_sock(struct socket_info* sock_info);
+int sctp_rcv_loop();
+int sctp_msg_send(struct dest_info* dst, char* buf, unsigned len);
+
+void destroy_sctp();
+
+#endif /* _sctp_server_h */

+ 73 - 0
socket_info.c

@@ -35,6 +35,7 @@
  *  2004-10-10  added grep_sock_info (andrei)
  *  2004-11-08  added find_si (andrei)
  *  2007-08-23  added detection for INADDR_ANY types of sockets (andrei)
+ *  2008-08-08  sctp support (andrei)
  */
 
 
@@ -103,6 +104,12 @@
 
 
 
+/* protocol order, filled by init_proto_order() */
+enum sip_protos nxt_proto[PROTO_LAST+1]=
+{ PROTO_UDP, PROTO_TCP, PROTO_TLS, PROTO_SCTP, 0 };
+
+
+
 /* another helper function, it just creates a socket_info struct */
 static inline struct socket_info* new_sock_info(	char* name,
 								unsigned short port, unsigned short proto,
@@ -157,6 +164,10 @@ static char* get_proto_name(unsigned short proto)
 #ifdef USE_TLS
 		case PROTO_TLS:
 			return "tls";
+#endif
+#ifdef USE_SCTP
+		case PROTO_SCTP:
+			return "sctp";
 #endif
 		default:
 			return "unknown";
@@ -180,6 +191,11 @@ static struct socket_info** get_sock_info_list(unsigned short proto)
 		case PROTO_TLS:
 			return &tls_listen;
 			break;
+#endif
+#ifdef USE_SCTP
+		case PROTO_SCTP:
+			return &sctp_listen;
+			break;
 #endif
 		default:
 			LOG(L_CRIT, "BUG: get_sock_info_list: invalid proto %d\n", proto);
@@ -775,6 +791,9 @@ int fix_all_socket_lists()
 #ifdef USE_TLS
 			&& (tls_listen==0)
 #endif
+#endif
+#ifdef USE_SCTP
+			&& (sctp_listen==0)
 #endif
 		){
 		/* get all listening ipv4 interfaces */
@@ -793,6 +812,13 @@ int fix_all_socket_lists()
 #endif
 			}
 #endif
+#ifdef USE_SCTP
+			if (!sctp_disable){
+				if (add_interfaces(0, AF_INET, 0,  PROTO_SCTP,
+									&sctp_listen)!=0)
+					goto error;
+			}
+#endif /* USE_SCTP */
 		}else{
 			/* if error fall back to get hostname */
 			/* get our address, only the first one */
@@ -836,12 +862,25 @@ int fix_all_socket_lists()
 	}
 #endif
 #endif
+#ifdef USE_SCTP
+	if (!sctp_disable && (fix_socket_list(&sctp_listen, &flags)!=0)){
+		LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list"
+				" sctp failed\n");
+		goto error;
+	}
+	if (flags){
+		socket_types|=flags|SOCKET_T_SCTP;
+	}
+#endif /* USE_SCTP */
 	if ((udp_listen==0)
 #ifdef USE_TCP
 			&& (tcp_listen==0)
 #ifdef USE_TLS
 			&& (tls_listen==0)
 #endif
+#endif
+#ifdef USE_SCTP
+			&& (sctp_listen==0)
 #endif
 		){
 		LOG(L_ERR, "ERROR: fix_all_socket_lists: no listening sockets\n");
@@ -885,3 +924,37 @@ void print_aliases()
 			printf("             %s: %.*s:*\n", get_proto_name(a->proto), 
 					a->alias.len, a->alias.s);
 }
+
+
+
+void init_proto_order()
+{
+	int r;
+	
+	/* fix proto list  (remove disabled protocols)*/
+#ifdef USE_TCP
+	if (tcp_disable)
+#endif
+		for(r=PROTO_NONE; r<=PROTO_LAST; r++){
+			if (nxt_proto[r]==PROTO_TCP)
+				nxt_proto[r]=nxt_proto[PROTO_TCP];
+		}
+#ifdef USE_TCP
+#ifdef USE_TLS
+	if (tls_disable || tcp_disable)
+#endif
+#endif
+		for(r=PROTO_NONE; r<=PROTO_LAST; r++){
+			if (nxt_proto[r]==PROTO_TLS)
+				nxt_proto[r]=nxt_proto[PROTO_TLS];
+		}
+#ifdef USE_SCTP
+	if (sctp_disable)
+		for(r=PROTO_NONE; r<=PROTO_LAST; r++){
+			if (nxt_proto[r]==PROTO_SCTP)
+				nxt_proto[r]=nxt_proto[PROTO_SCTP];
+		}
+#endif
+}
+
+

+ 27 - 29
socket_info.h

@@ -32,6 +32,7 @@
  * History:
  * --------
  *  2003-10-22  created by andrei
+ *  2008-08-08  sctp support (andrei)
  */
 
 
@@ -50,17 +51,26 @@ extern struct socket_info* tcp_listen;
 #ifdef USE_TLS
 extern struct socket_info* tls_listen;
 #endif
+#ifdef USE_SCTP
+extern struct socket_info* sctp_listen;
+#endif
+
+extern enum sip_protos nxt_proto[PROTO_LAST+1];
+
 
 
 /* flags for finding out the address types */
-#define SOCKET_T_IPV4 1
-#define SOCKET_T_IPV6 2
-#define SOCKET_T_UDP  4
-#define SOCKET_T_TCP  8
-#define SOCKET_T_TLS 16
+#define SOCKET_T_IPV4  1
+#define SOCKET_T_IPV6  2
+#define SOCKET_T_UDP   4
+#define SOCKET_T_TCP   8
+#define SOCKET_T_TLS  16
+#define SOCKET_T_SCTP 32
 
 extern int socket_types;
 
+void init_proto_order();
+
 int add_listen_iface(char* name, unsigned short port, unsigned short proto,
 							enum si_flags flags);
 int fix_all_socket_lists();
@@ -74,35 +84,18 @@ struct socket_info* grep_sock_info_by_port(unsigned short port,
 struct socket_info* find_si(struct ip_addr* ip, unsigned short port,
 												unsigned short proto);
 
+
+
 /* helper function:
  * returns next protocol, if the last one is reached return 0
- * useful for cycling on the supported protocols */
+ * useful for cycling on the supported protocols
+ * order: udp, tcp, tls, sctp */
 static inline int next_proto(unsigned short proto)
 {
-	switch(proto){
-		case PROTO_NONE:
-			return PROTO_UDP;
-		case PROTO_UDP:
-#ifdef	USE_TCP
-			return (tcp_disable)?0:PROTO_TCP;
-#else
-			return 0;
-#endif
-#ifdef USE_TCP
-		case PROTO_TCP:
-#ifdef USE_TLS
-			return (tls_disable)?0:PROTO_TLS;
-#else
-			return 0;
-#endif
-#endif
-#ifdef USE_TLS
-		case PROTO_TLS:
-			return 0;
-#endif
-		default:
+	if (proto>PROTO_LAST)
 			LOG(L_ERR, "ERROR: next_proto: unknown proto %d\n", proto);
-	}
+	else
+		return nxt_proto[proto];
 	return 0;
 }
 
@@ -116,6 +109,11 @@ inline static struct socket_info* get_first_socket()
 	if (udp_listen) return udp_listen;
 #ifdef USE_TCP
 	else if (tcp_listen) return tcp_listen;
+#endif
+#ifdef USE_SCTP
+	else if (sctp_listen) return sctp_listen;
+#endif
+#ifdef USE_TCP
 #ifdef USE_TLS
 	else if (tls_listen) return tls_listen;
 #endif