sctp_server.c
c3611173
 /* 
  * $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)
047b1dfb
  *  2009-02-27  blacklist support (andrei)
7a66235b
  *  2009-04-28  sctp stats & events macros (andrei)
c3611173
  */
 
1d0661db
 /*!
  * \file
  * \brief SIP-router core :: 
  * \ingroup core
  * Module: \ref core
  */
 
c3611173
 #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>
 
 
cc164956
 #include "sctp_sockopts.h"
c3611173
 #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"
047b1dfb
 #ifdef USE_DST_BLACKLIST
 #include "dst_blacklist.h"
 #endif /* USE_DST_BLACKLIST */
31fd952b
 #include "timer_ticks.h"
 #include "clist.h"
 #include "error.h"
 #include "timer.h"
7a66235b
 #include "sctp_stats.h"
e11d7294
 #include "sctp_ev.h"
c3611173
 
 
 
31fd952b
 static atomic_t* sctp_conn_no;
 
b73d58cf
 
92b6de79
 #define ABORT_REASON_MAX_ASSOCS \
 	"Maximum configured number of open associations exceeded"
b73d58cf
 
c8e95587
 /* check if the underlying OS supports sctp
    returns 0 if yes, -1 on error */
 int sctp_check_support()
 {
 	int s;
23ca6c06
 	char buf[256];
 	
c8e95587
 	s = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
 	if (s!=-1){
 		close(s);
23ca6c06
 		if (sctp_check_compiled_sockopts(buf, sizeof(buf))!=0){
 			LOG(L_WARN, "WARNING: sctp: your ser version was compiled"
 						" without support for the following sctp options: %s"
602a2344
 						", which might cause unforseen problems \n", buf);
23ca6c06
 			LOG(L_WARN, "WARNING: sctp: please consider recompiling ser with"
 						" an upgraded sctp library version\n");
 		}
c8e95587
 		return 0;
 	}
 	return -1;
 }
 
 
 
23ca6c06
 /* append a token to a buffer (uses space between tokens) */
 inline static void append_tok2buf(char* buf, int blen, char* tok)
 {
 	char* p;
 	char* end;
 	int len;
 	
 	if (buf && blen){
 		end=buf+blen;
 		p=memchr(buf, 0, blen);
 		if (p==0) goto error;
 		if (p!=buf && p<(end-1)){
 			*p=' ';
 			p++;
 		}
 		len=MIN_int(strlen(tok), end-1-p);
 		memcpy(p, tok, len);
 		p[len]=0;
 	}
 error:
 	return;
 }
 
 
 
 /* check if support fot all the needed sockopts  was compiled;
    an ascii list of the unsuported options is returned in buf
    returns 0 on success and  -number of unsuported options on failure
    (<0 on failure)
 */
 int sctp_check_compiled_sockopts(char* buf, int size)
 {
 	int err;
 
 	err=0;
 	if (buf && (size>0)) *buf=0; /* "" */
 #ifndef SCTP_FRAGMENT_INTERLEAVE
 	err++;
 	append_tok2buf(buf, size, "SCTP_FRAGMENT_INTERLEAVE");
 #endif
 #ifndef SCTP_PARTIAL_DELIVERY_POINT
 	err++;
 	append_tok2buf(buf, size, "SCTP_PARTIAL_DELIVERY_POINT");
 #endif
 #ifndef SCTP_NODELAY
 	err++;
 	append_tok2buf(buf, size, "SCTP_NODELAY");
 #endif
 #ifndef SCTP_DISABLE_FRAGMENTS
 	err++;
 	append_tok2buf(buf, size, "SCTP_DISABLE_FRAGMENTS");
 #endif
 #ifndef SCTP_AUTOCLOSE
 	err++;
 	append_tok2buf(buf, size, "SCTP_AUTOCLOSE");
 #endif
 #ifndef SCTP_EVENTS
 	err++;
 	append_tok2buf(buf, size, "SCTP_EVENTS");
 #endif
 	
 	return -err;
 }
 
 
 
aec3d242
 /* init all the sockaddr_union members of the socket_info struct
    returns 0 on success and -1 on error */
 inline static int sctp_init_su(struct socket_info* sock_info)
c3611173
 {
 	union sockaddr_union* addr;
66b00f85
 	struct addr_info* ai;
c3611173
 	
 	addr=&sock_info->su;
 	if (init_su(addr, &sock_info->address, sock_info->port_no)<0){
aec3d242
 		LOG(L_ERR, "ERROR: sctp_init_su: could not init sockaddr_union for"
66b00f85
 					"primary sctp address %.*s:%d\n",
 					sock_info->address_str.len, sock_info->address_str.s,
 					sock_info->port_no );
c3611173
 		goto error;
 	}
66b00f85
 	for (ai=sock_info->addr_info_lst; ai; ai=ai->next)
 		if (init_su(&ai->su, &ai->address, sock_info->port_no)<0){
aec3d242
 			LOG(L_ERR, "ERROR: sctp_init_su: could not init"
66b00f85
 					"backup sctp sockaddr_union for %.*s:%d\n",
 					ai->address_str.len, ai->address_str.s,
 					sock_info->port_no );
 			goto error;
 		}
aec3d242
 	return 0;
 error:
 	return -1;
 }
c3611173
 
aec3d242
 
 
5b4d2288
 /** set a socket option (wrapper over setsockopt).
   * @param err_prefix - if 0 no error message is printed on failure, if !=0
   *                     it will be prepended to the error message.
   * @return 0 on success, -1 on error */
b73d58cf
 int sctp_setsockopt(int s, int level, int optname, 
 					void* optval, socklen_t optlen, char* err_prefix)
 {
 	if (setsockopt(s, level, optname, optval, optlen) ==-1){
 		if (err_prefix)
 			ERR("%s: %s [%d]\n", err_prefix, strerror(errno), errno);
 		return -1;
 	}
 	return 0;
 }
 
 
 
 /** get a socket option (wrapper over getsockopt).
   * @param err_prefix - if 0 no error message is printed on failure, if !=0
   *                     it will be prepended to the error message.
   * @return 0 on success, -1 on error */
 int sctp_getsockopt(int s, int level, int optname, 
 					void* optval, socklen_t* optlen, char* err_prefix)
5b4d2288
 {
b73d58cf
 	if (getsockopt(s, level, optname, optval, optlen) ==-1){
5b4d2288
 		if (err_prefix)
 			ERR("%s: %s [%d]\n", err_prefix, strerror(errno), errno);
 		return -1;
 	}
 	return 0;
 }
 
 
 
b73d58cf
 /** get the os defaults for cfg options with os correspondents.
  *  @param cfg - filled with the os defaults
  *  @return -1 on error, 0 on success
  */
 int sctp_get_os_defaults(struct cfg_group_sctp* cfg)
 {
cc164956
 	int s;
 	int ret;
 	
 	s = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
 	if (s==-1)
 		return -1;
 	ret=sctp_get_cfg_from_sock(s, cfg);
 	close(s);
 	return ret;
 }
 
 
 
 /** get the os cfg options from a specific socket.
  *  @param s - intialized sctp socket
  *  @param cfg - filled with the os defaults
  *  @return -1 on error, 0 on success
  */
 int sctp_get_cfg_from_sock(int s, struct cfg_group_sctp* cfg)
 {
b73d58cf
 	int optval;
 	socklen_t optlen;
 #ifdef SCTP_RTOINFO
 	struct sctp_rtoinfo rto;
 #endif /* SCTP_RTOINFO */
2c552c8d
 #ifdef SCTP_ASSOCINFO
 	struct sctp_assocparams ap;
 #endif /* SCTP_ASSOCINFO */
 #ifdef SCTP_INITMSG
 	struct sctp_initmsg im;
 #endif /* SCTP_INITMSG */
 #ifdef SCTP_PEER_ADDR_PARAMS
 	struct sctp_paddrparams pp;
 #endif /* SCTP_PEER_ADDR_PARAMS */
cc164956
 #ifdef	SCTP_DELAYED_SACK
 	struct sctp_sack_info sack_info;
 #endif	/* SCTP_DELAYED_SACK */
 #ifdef	SCTP_DELAYED_ACK_TIME
 	struct sctp_assoc_value sack_val; /* old version */
 #endif /* SCTP_DELAYED_ACK_TIME */
2c552c8d
 #ifdef SCTP_MAX_BURST
 	struct sctp_assoc_value av;
 #endif /* SCTP_MAX_BURST */
b73d58cf
 	
 	/* SO_RCVBUF */
 	optlen=sizeof(int);
 	if (sctp_getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&optval,
 							&optlen, "SO_RCVBUF")==0){
 		/* success => hack to set the "default" values*/
 		#ifdef __OS_linux
 			optval/=2; /* in linux getsockopt() returns 2*set_value */
 		#endif
 		cfg->so_rcvbuf=optval;
 	}
 	/* SO_SNDBUF */
 	optlen=sizeof(int);
 	if (sctp_getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&optval,
 							&optlen, "SO_SNDBUF")==0){
 		/* success => hack to set the "default" values*/
 		#ifdef __OS_linux
 			optval/=2; /* in linux getsockopt() returns 2*set_value */
 		#endif
 		cfg->so_sndbuf=optval;
 	}
cc164956
 	/* SCTP_AUTOCLOSE */
 #ifdef SCTP_AUTOCLOSE
 	optlen=sizeof(int);
 	if (sctp_getsockopt(s, IPPROTO_SCTP, SCTP_AUTOCLOSE, (void*)&optval,
 							&optlen, "SCTP_AUTOCLOSE")==0){
 		cfg->autoclose=optval;
 	}
 #endif /* SCTP_AUTOCLOSE */
b73d58cf
 	/* SCTP_RTOINFO -> srto_initial, srto_min, srto_max */
 #ifdef SCTP_RTOINFO
 	optlen=sizeof(rto);
 	rto.srto_assoc_id=0;
 	if (sctp_getsockopt(s, IPPROTO_SCTP, SCTP_RTOINFO, (void*)&rto,
 							&optlen, "SCTP_RTOINFO")==0){
 		/* success => hack to set the "default" values*/
 		cfg->srto_initial=rto.srto_initial;
 		cfg->srto_min=rto.srto_min;
 		cfg->srto_max=rto.srto_max;
 	}
 #endif /* SCTP_RTOINFO */
2c552c8d
 #ifdef SCTP_ASSOCINFO
 	optlen=sizeof(ap);
 	ap.sasoc_assoc_id=0;
 	if (sctp_getsockopt(s, IPPROTO_SCTP, SCTP_ASSOCINFO, (void*)&ap,
 							&optlen, "SCTP_ASSOCINFO")==0){
 		/* success => hack to set the "default" values*/
 		cfg->asocmaxrxt=ap.sasoc_asocmaxrxt;
 	}
 #endif /* SCTP_ASSOCINFO */
 #ifdef SCTP_INITMSG
 	optlen=sizeof(im);
 	if (sctp_getsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, (void*)&im,
 							&optlen, "SCTP_INITMSG")==0){
 		/* success => hack to set the "default" values*/
 		cfg->init_max_attempts=im.sinit_max_attempts;
 		cfg->init_max_timeo=im.sinit_max_init_timeo;
 	}
 #endif /* SCTP_INITMSG */
 #ifdef SCTP_PEER_ADDR_PARAMS
 	optlen=sizeof(pp);
 	memset(&pp, 0, sizeof(pp)); /* get defaults */
6deb1c53
 	/* set the AF, needed on older linux kernels even for INADDR_ANY */
 	pp.spp_address.ss_family=AF_INET;
2c552c8d
 	if (sctp_getsockopt(s, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, (void*)&pp,
 							&optlen, "SCTP_PEER_ADDR_PARAMS")==0){
 		/* success => hack to set the "default" values*/
 		cfg->hbinterval=pp.spp_hbinterval;
 		cfg->pathmaxrxt=pp.spp_pathmaxrxt;
 	}
 #endif /* SCTP_PEER_ADDR_PARAMS */
 #if defined SCTP_DELAYED_SACK || defined SCTP_DELAYED_ACK_TIME
 #ifdef SCTP_DELAYED_SACK
cc164956
 	optlen=sizeof(sack_info);
 	memset(&sack_info, 0, sizeof(sack_info));
 	if (sctp_getsockopt(s, IPPROTO_SCTP, SCTP_DELAYED_SACK, (void*)&sack_info,
 							&optlen, 0)==0){
2c552c8d
 		/* success => hack to set the "default" values*/
cc164956
 		cfg->sack_delay=sack_info.sack_delay;
 		cfg->sack_freq=sack_info.sack_freq;
 	}else
2c552c8d
 #endif /* SCTP_DELAYED_SACK */
cc164956
 	{
 #ifdef	SCTP_DELAYED_ACK_TIME
 		optlen=sizeof(sack_val);
 		memset(&sack_val, 0, sizeof(sack_val));
 		/* if no SCTP_DELAYED_SACK supported by the sctp lib, or setting it
 		   failed (not supported by the kernel) try using the obsolete
 		   SCTP_DELAYED_ACK_TIME method */
 		if (sctp_getsockopt(s, IPPROTO_SCTP, SCTP_DELAYED_ACK_TIME,
 								(void*)&sack_val, &optlen, 
 								"SCTP_DELAYED_ACK_TIME")==0){
 			/* success => hack to set the "default" values*/
 			cfg->sack_delay=sack_val.assoc_value;
 			cfg->sack_freq=0; /* unknown */
 		}
 #else	/* SCTP_DELAYED_ACK_TIME */
 		/* no SCTP_DELAYED_ACK_TIME support and SCTP_DELAYED_SACK failed
 		   => error */
 		ERR("cfg: SCTP_DELAYED_SACK: %s [%d]\n", strerror(errno), errno);
 #endif /* SCTP_DELAYED_ACK_TIME */
 	}
2c552c8d
 #endif /* SCTP_DELAYED_SACK  | SCTP_DELAYED_ACK_TIME*/
 #ifdef SCTP_MAX_BURST
 	optlen=sizeof(av);
 	av.assoc_id=0;
 	if (sctp_getsockopt(s, IPPROTO_SCTP, SCTP_MAX_BURST, (void*)&av,
 							&optlen, "SCTP_MAX_BURST")==0){
 		/* success => hack to set the "default" values*/
 		cfg->max_burst=av.assoc_value;
 	}
 #endif /* SCTP_MAX_BURST */
 	
b73d58cf
 	return 0;
 }
 
 
 
aec3d242
 /* set common (for one to many and one to one) sctp socket options
51f98d74
    tries to ignore non-critical errors (it will only log them), for
    improved portability (for example older linux kernel version support
    only a limited number of sctp socket options)
23ca6c06
    returns 0 on success, -1 on error
    WARNING: please keep it sync'ed w/ sctp_check_compiled_sockopts() */
6deb1c53
 static int sctp_init_sock_opt_common(int s, int af)
aec3d242
 {
 	int optval;
f9437a7d
 	int pd_point;
 	int saved_errno;
aec3d242
 	socklen_t optlen;
5051ae80
 	int sctp_err;
b73d58cf
 #ifdef SCTP_RTOINFO
 	struct sctp_rtoinfo rto;
 #endif /* SCTP_RTOINFO */
2c552c8d
 #ifdef SCTP_ASSOCINFO
 	struct sctp_assocparams ap;
 #endif /* SCTP_ASSOCINFO */
 #ifdef SCTP_INITMSG
 	struct sctp_initmsg im;
 #endif /* SCTP_INITMSG */
 #ifdef SCTP_PEER_ADDR_PARAMS
 	struct sctp_paddrparams pp;
 #endif /* SCTP_PEER_ADDR_PARAMS */
 #ifdef SCTP_DELAYED_SACK
cc164956
 	struct sctp_sack_info sack_info;
 #endif	/* SCTP_DELAYED_SACK */
 #ifdef	SCTP_DELAYED_ACK_TIME
 	struct sctp_assoc_value sack_val;
 #endif /* defined SCTP_DELAYED_ACK_TIME */
2c552c8d
 #ifdef SCTP_MAX_BURST
 	struct sctp_assoc_value av;
 #endif /* SCTP_MAX_BURST */
 	
e5064a81
 #ifdef __OS_linux
 	union {
 		struct sctp_event_subscribe s;
 		char padding[sizeof(struct sctp_event_subscribe)+sizeof(__u8)];
 	} es;
 #else
 	struct sctp_event_subscribe es;
 #endif
 	struct sctp_event_subscribe* ev_s;
aec3d242
 	
5051ae80
 	sctp_err=0;
e5064a81
 #ifdef __OS_linux
 	ev_s=&es.s;
 #else
 	ev_s=&es;
 #endif
51f98d74
 	/* set tos */
 	optval = tos;
 	if (setsockopt(s, IPPROTO_IP, IP_TOS, (void*)&optval,sizeof(optval)) ==-1){
 		LOG(L_WARN, "WARNING: sctp_init_sock_opt_common: setsockopt tos: %s\n",
 				strerror(errno));
 		/* continue since this is not critical */
 	}
 	
c3611173
 	/* set receive buffer: SO_RCVBUF*/
57f57a43
 	if (cfg_get(sctp, sctp_cfg, so_rcvbuf)){
 		optval=cfg_get(sctp, sctp_cfg, so_rcvbuf);
aec3d242
 		if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
c3611173
 					(void*)&optval, sizeof(optval)) ==-1){
aec3d242
 			LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt:"
 						" SO_RCVBUF (%d): %s\n", optval, strerror(errno));
c3611173
 			/* continue, non-critical */
 		}
 	}
 	
 	/* set send buffer: SO_SNDBUF */
57f57a43
 	if (cfg_get(sctp, sctp_cfg, so_sndbuf)){
 		optval=cfg_get(sctp, sctp_cfg, so_sndbuf);
aec3d242
 		if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
c3611173
 					(void*)&optval, sizeof(optval)) ==-1){
aec3d242
 			LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt:"
 						" SO_SNDBUF (%d): %s\n", optval, strerror(errno));
c3611173
 			/* continue, non-critical */
 		}
 	}
 	
235631a0
 	/* set reuseaddr */
 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
 						(void*)&optval, sizeof(optval))==-1){
 			LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt:"
 						" SO_REUSEADDR (%d): %s\n", optval, strerror(errno));
 			/* continue, non-critical */
 	}
 
 	
c3611173
 	/* disable fragments interleave (SCTP_FRAGMENT_INTERLEAVE) --
 	 * we don't want partial delivery, so fragment interleave must be off too
 	 */
23ca6c06
 #ifdef SCTP_FRAGMENT_INTERLEAVE
c3611173
 	optval=0;
aec3d242
 	if (setsockopt(s, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE ,
c3611173
 					(void*)&optval, sizeof(optval)) ==-1){
aec3d242
 		LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
 					"SCTP_FRAGMENT_INTERLEAVE: %s\n", strerror(errno));
5051ae80
 		sctp_err++;
51f98d74
 		/* try to continue */
c3611173
 	}
602a2344
 #else
 #warning no sctp lib support for SCTP_FRAGMENT_INTERLEAVE, consider upgrading
23ca6c06
 #endif /* SCTP_FRAGMENT_INTERLEAVE */
c3611173
 	
 	/* 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) */
23ca6c06
 #ifdef SCTP_PARTIAL_DELIVERY_POINT
c3611173
 	optlen=sizeof(optval);
aec3d242
 	if (getsockopt(s, SOL_SOCKET, SO_RCVBUF,
c3611173
 					(void*)&optval, &optlen) ==-1){
aec3d242
 		LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: getsockopt: "
 						"SO_RCVBUF: %s\n", strerror(errno));
51f98d74
 		/* try to continue */
 		optval=0;
c3611173
 	}
f9437a7d
 #ifdef __OS_linux
 	optval/=2; /* in linux getsockopt() returns twice the set value */
 #endif
 	pd_point=optval;
 	saved_errno=0;
 	while(pd_point &&
 			setsockopt(s, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT,
 					(void*)&pd_point, sizeof(pd_point)) ==-1){
 		if (!saved_errno)
 			saved_errno=errno;
 		pd_point--;
 	}
 	
 	if (pd_point!=optval){
 		if (pd_point==0){
 			/* all attempts failed */
 			LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
aec3d242
 						"SCTP_PARTIAL_DELIVERY_POINT (%d): %s\n",
 						optval, strerror(errno));
f9437a7d
 			sctp_err++;
 			/* try to continue */
 		}else{
 			/* success but to a lower value (might not be disabled) */
 			LOG(L_WARN, "setsockopt SCTP_PARTIAL_DELIVERY_POINT set to %d, but"
 				" the socket rcvbuf is %d (higher values fail with"
 				" \"%s\" [%d])\n",
 				pd_point, optval, strerror(saved_errno), saved_errno);
 		}
c3611173
 	}
602a2344
 #else
 #warning no sctp lib support for SCTP_PARTIAL_DELIVERY_POINT, consider upgrading
23ca6c06
 #endif /* SCTP_PARTIAL_DELIVERY_POINT */
c3611173
 	
 	/* nagle / no delay */
23ca6c06
 #ifdef SCTP_NODELAY
c3611173
 	optval=1;
aec3d242
 	if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY,
c3611173
 					(void*)&optval, sizeof(optval)) ==-1){
aec3d242
 		LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
 						"SCTP_NODELAY: %s\n", strerror(errno));
5051ae80
 		sctp_err++;
c3611173
 		/* non critical, try to continue */
 	}
602a2344
 #else
 #warning no sctp lib support for SCTP_NODELAY, consider upgrading
23ca6c06
 #endif /* SCTP_NODELAY */
c3611173
 	
 	/* enable message fragmentation (SCTP_DISABLE_FRAGMENTS)  (on send) */
23ca6c06
 #ifdef SCTP_DISABLE_FRAGMENTS
c3611173
 	optval=0;
aec3d242
 	if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS,
c3611173
 					(void*)&optval, sizeof(optval)) ==-1){
aec3d242
 		LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
 						"SCTP_DISABLE_FRAGMENTS: %s\n", strerror(errno));
5051ae80
 		sctp_err++;
c3611173
 		/* non critical, try to continue */
 	}
602a2344
 #else
 #warning no sctp lib support for SCTP_DISABLE_FRAGMENTS, consider upgrading
23ca6c06
 #endif /* SCTP_DISABLE_FRAGMENTS */
c3611173
 	
 	/* set autoclose */
23ca6c06
 #ifdef SCTP_AUTOCLOSE
57f57a43
 	optval=cfg_get(sctp, sctp_cfg, autoclose);
23ca6c06
 	if (setsockopt(s, IPPROTO_SCTP, SCTP_AUTOCLOSE,
c3611173
 					(void*)&optval, sizeof(optval)) ==-1){
aec3d242
 		LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
23ca6c06
 						"SCTP_AUTOCLOSE: %s (critical)\n", strerror(errno));
 		/* critical: w/o autoclose we could have sctp connection living
 		   forever (if the remote side doesn't close them) */
5051ae80
 		sctp_err++;
23ca6c06
 		goto error;
c3611173
 	}
23ca6c06
 #else
 #error SCTP_AUTOCLOSE not supported, please upgrade your sctp library
 #endif /* SCTP_AUTOCLOSE */
b73d58cf
 	/* set rtoinfo options: srto_initial, srto_min, srto_max */
 #ifdef SCTP_RTOINFO
 	memset(&rto, 0, sizeof(rto));
 	rto.srto_initial=cfg_get(sctp, sctp_cfg, srto_initial);
 	rto.srto_min=cfg_get(sctp, sctp_cfg, srto_min);
 	rto.srto_max=cfg_get(sctp, sctp_cfg, srto_max);
 	if (rto.srto_initial || rto.srto_min || rto.srto_max){
 		/* if at least one is non-null => we have to set it */
 		if (sctp_setsockopt(s, IPPROTO_SCTP, SCTP_RTOINFO, (void*)&rto,
 							sizeof(rto), "setsockopt: SCTP_RTOINFO")!=0){
 			sctp_err++;
 			/* non critical, try to continue */
 		}
 	}
 #else
 #warning no sctp lib support for SCTP_RTOINFO, consider upgrading
 #endif /* SCTP_RTOINFO */
2c552c8d
 	/* set associnfo options: assocmaxrxt */
 #ifdef SCTP_ASSOCINFO
 	memset(&ap, 0, sizeof(ap));
 	ap.sasoc_asocmaxrxt=cfg_get(sctp, sctp_cfg, asocmaxrxt);
 	if (ap.sasoc_asocmaxrxt){
 		/* if at least one is non-null => we have to set it */
 		if (sctp_setsockopt(s, IPPROTO_SCTP, SCTP_ASSOCINFO, (void*)&ap,
 							sizeof(ap), "setsockopt: SCTP_ASSOCINFO")!=0){
 			sctp_err++;
 			/* non critical, try to continue */
 		}
 	}
 #else
 #warning no sctp lib support for SCTP_ASSOCINFO, consider upgrading
 #endif /* SCTP_ASOCINFO */
 	/* set initmsg options: init_max_attempts & init_max_init_timeo */
 #ifdef SCTP_INITMSG
 	memset(&im, 0, sizeof(im));
 	im.sinit_max_attempts=cfg_get(sctp, sctp_cfg, init_max_attempts);
 	im.sinit_max_init_timeo=cfg_get(sctp, sctp_cfg, init_max_timeo);
 	if (im.sinit_max_attempts || im.sinit_max_init_timeo){
 		/* if at least one is non-null => we have to set it */
 		if (sctp_setsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, (void*)&im,
 							sizeof(im), "setsockopt: SCTP_INITMSG")!=0){
 			sctp_err++;
 			/* non critical, try to continue */
 		}
 	}
 #else
 #warning no sctp lib support for SCTP_INITMSG, consider upgrading
 #endif /* SCTP_INITMSG */
 	/* set sctp peer addr options: hbinterval & pathmaxrxt */
 #ifdef SCTP_PEER_ADDR_PARAMS
 	memset(&pp, 0, sizeof(pp));
6deb1c53
 	pp.spp_address.ss_family=af;
2c552c8d
 	pp.spp_hbinterval=cfg_get(sctp, sctp_cfg, hbinterval);
 	pp.spp_pathmaxrxt=cfg_get(sctp, sctp_cfg, pathmaxrxt);
 	if (pp.spp_hbinterval || pp.spp_pathmaxrxt){
 		if (pp.spp_hbinterval > 0)
 			pp.spp_flags=SPP_HB_ENABLE;
 		else if (pp.spp_hbinterval==-1){
 			pp.spp_flags=SPP_HB_DISABLE;
 			pp.spp_hbinterval=0;
 		}
8459110a
 #ifdef __OS_linux
 		if (pp.spp_pathmaxrxt){
 			/* hack to work on linux, pathmaxrxt is set only if
 			   SPP_PMTUD_ENABLE */
 			pp.spp_flags|=SPP_PMTUD_ENABLE;
 		}
 #endif /*__OS_linux */
2c552c8d
 		/* if at least one is non-null => we have to set it */
 		if (sctp_setsockopt(s, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, (void*)&pp,
 						sizeof(pp), "setsockopt: SCTP_PEER_ADDR_PARAMS")!=0){
 			sctp_err++;
 			/* non critical, try to continue */
 		}
 	}
 #else
 #warning no sctp lib support for SCTP_PEER_ADDR_PARAMS, consider upgrading
 #endif /* SCTP_PEER_ADDR_PARAMS */
 	/* set delayed ack options: sack_delay & sack_freq */
 #if defined SCTP_DELAYED_SACK || defined SCTP_DELAYED_ACK_TIME
 #ifdef SCTP_DELAYED_SACK
cc164956
 	memset(&sack_info, 0, sizeof(sack_info));
 	sack_info.sack_delay=cfg_get(sctp, sctp_cfg, sack_delay);
 	sack_info.sack_freq=cfg_get(sctp, sctp_cfg, sack_freq);
 	if ((sack_info.sack_delay || sack_info.sack_freq) &&
 		(sctp_setsockopt(s, IPPROTO_SCTP, SCTP_DELAYED_SACK,
 							(void*)&sack_info, sizeof(sack_info), 0)!=0)) {
 		/* if setting SCTP_DELAYED_SACK failed, try the old obsolete
 		   SCTP_DELAYED_ACK_TIME */
 #endif /* SCTP_DELAYED_SACK */
 #ifdef SCTP_DELAYED_ACK_TIME
 		memset(&sack_val, 0, sizeof(sack_val));
 		sack_val.assoc_value=cfg_get(sctp, sctp_cfg, sack_delay);
 		if (sack_val.assoc_value){
 			if (sctp_setsockopt(s, IPPROTO_SCTP, SCTP_DELAYED_ACK_TIME,
 									(void*)&sack_val, sizeof(sack_val),
 									"setsockopt: SCTP_DELAYED_ACK_TIME")!=0){
 				sctp_err++;
 				/* non critical, try to continue */
 			}
 		}
 #else /* SCTP_DELAYED_ACK_TIME */
 		/* no SCTP_DELAYED_ACK_TIME support and SCTP_DELAYED_SACK failed
 		   => error */
 		if (sack_info.sack_delay){
2c552c8d
 			sctp_err++;
cc164956
 			ERR("cfg: setting SCTP_DELAYED_SACK: %s [%d]\n",
 						strerror(errno), errno);
2c552c8d
 		}
cc164956
 #endif /* SCTP_DELAYED_ACK_TIME */
 		if (cfg_get(sctp, sctp_cfg, sack_freq)){
 #ifdef SCTP_DELAYED_SACK
2c552c8d
 			sctp_err++;
cc164956
 			WARN("could not set sctp sack_freq, please upgrade your kernel\n");
 #else /* SCTP_DELAYED_SACK */
 			WARN("could not set sctp sack_freq, please upgrade your sctp"
 					" library\n");
 #endif /* SCTP_DELAYED_SACK */
 			((struct cfg_group_sctp*)sctp_cfg)->sack_freq=0;
2c552c8d
 		}
cc164956
 #ifdef SCTP_DELAYED_SACK
2c552c8d
 	}
 #endif /* SCTP_DELAYED_SACK */
cc164956
 	
 #else /* SCTP_DELAYED_SACK  | SCTP_DELAYED_ACK_TIME*/
2c552c8d
 #warning no sctp lib support for SCTP_DELAYED_SACK, consider upgrading
 #endif /* SCTP_DELAYED_SACK  | SCTP_DELAYED_ACK_TIME*/
 	/* set max burst option */
 #ifdef SCTP_MAX_BURST
 	memset(&av, 0, sizeof(av));
 	av.assoc_value=cfg_get(sctp, sctp_cfg, max_burst);
 	if (av.assoc_value){
 		if (sctp_setsockopt(s, IPPROTO_SCTP, SCTP_MAX_BURST, (void*)&av,
 							sizeof(av), "setsockopt: SCTP_MAX_BURST")!=0){
 			sctp_err++;
 			/* non critical, try to continue */
 		}
 	}
 #else
 #warning no sctp lib support for SCTP_MAX_BURST, consider upgrading
 #endif /* SCTP_MAX_BURST */
c3611173
 	
4a4dd08f
 	memset(&es, 0, sizeof(es));
c3611173
 	/* SCTP_EVENTS for SCTP_SNDRCV (sctp_data_io_event) -> per message
 	 *  information in sctp_sndrcvinfo */
e5064a81
 	ev_s->sctp_data_io_event=1;
4a4dd08f
 	/* enable association event notifications */
e5064a81
 	ev_s->sctp_association_event=1; /* SCTP_ASSOC_CHANGE */
 	ev_s->sctp_address_event=1;  /* enable address events notifications */
 	ev_s->sctp_send_failure_event=1; /* SCTP_SEND_FAILED */
 	ev_s->sctp_peer_error_event=1;   /* SCTP_REMOTE_ERROR */
 	ev_s->sctp_shutdown_event=1;     /* SCTP_SHUTDOWN_EVENT */
 	ev_s->sctp_partial_delivery_event=1; /* SCTP_PARTIAL_DELIVERY_EVENT */
 	/* ev_s->sctp_adaptation_layer_event=1; - not supported by lksctp<=1.0.6*/
 	/* ev_s->sctp_authentication_event=1; -- not supported on linux 2.6.25 */
4a4dd08f
 	
 	/* enable the SCTP_EVENTS */
23ca6c06
 #ifdef SCTP_EVENTS
e5064a81
 	if (setsockopt(s, IPPROTO_SCTP, SCTP_EVENTS, ev_s, sizeof(*ev_s))==-1){
 		/* on linux the checks for the struct sctp_event_subscribe size
 		   are too strict, making certain lksctp/kernel combination
 		   unworkable => since we don't use the extra information
 		   (sctp_authentication_event) added in newer version, we can
 		   try with different sizes) */
 #ifdef __OS_linux
 		/* 1. lksctp 1.0.9 with kernel < 2.6.26 -> kernel expects 
 		      the structure without the authentication event member */
 		if (setsockopt(s, IPPROTO_SCTP, SCTP_EVENTS, ev_s, sizeof(*ev_s)-1)==0)
 			goto ev_success;
 		/* 2. lksctp < 1.0.9? with kernel >= 2.6.26: the sctp.h structure
 		   does not have the authentication member, but the newer kernels 
 		   check only for optlen > sizeof(...) => we should never reach
 		   this point. */
 		/* 3. just to be foolproof if we reached this point, try
 		    with a bigger size before giving up  (out of desperation) */
 		if (setsockopt(s, IPPROTO_SCTP, SCTP_EVENTS, ev_s, sizeof(es))==0)
 			goto ev_success;
 
 #endif
aec3d242
 		LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
 				"SCTP_EVENTS: %s\n", strerror(errno));
5051ae80
 		sctp_err++;
e5064a81
 		goto error; /* critical */
4a4dd08f
 	}
e5064a81
 #ifdef __OS_linux
 ev_success:
 #endif
602a2344
 #else
e5064a81
 #error no sctp lib support for SCTP_EVENTS, consider upgrading
23ca6c06
 #endif /* SCTP_EVENTS */
c3611173
 	
5051ae80
 	if (sctp_err){
 		LOG(L_ERR, "ERROR: sctp: setting some sctp sockopts failed, "
 					"consider upgrading your kernel\n");
 	}
aec3d242
 	return 0;
23ca6c06
 error:
aec3d242
 	return -1;
 }
 
 
 
 /* bind all addresses from sock (sockaddr_unions)
    returns 0 on success, .1 on error */
 static int sctp_bind_sock(struct socket_info* sock_info)
 {
 	struct addr_info* ai;
 	union sockaddr_union* addr;
c3611173
 	
aec3d242
 	addr=&sock_info->su;
 	/* bind the addresses*/
c3611173
 	if (bind(sock_info->socket,  &addr->s, sockaddru_len(*addr))==-1){
aec3d242
 		LOG(L_ERR, "ERROR: sctp_bind_sock: bind(%x, %p, %d) on %s: %s\n",
c3611173
 				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)
aec3d242
 			LOG(L_ERR, "ERROR: sctp_bind_sock: might be caused by using a "
c3611173
 							"link local address, try site local or global\n");
 	#endif
 		goto error;
 	}
66b00f85
 	for (ai=sock_info->addr_info_lst; ai; ai=ai->next)
 		if (sctp_bindx(sock_info->socket, &ai->su.s, 1, SCTP_BINDX_ADD_ADDR)
 					==-1){
aec3d242
 			LOG(L_ERR, "ERROR: sctp_bind_sock: sctp_bindx(%x, %.*s:%d, 1, ...)"
66b00f85
 						" on %s:%d : [%d] %s (trying to continue)\n",
 						sock_info->socket,
 						ai->address_str.len, ai->address_str.s, 
 						sock_info->port_no,
 						sock_info->address_str.s, sock_info->port_no,
 						errno, strerror(errno));
 		#ifdef USE_IPV6
 			if (ai->su.s.sa_family==AF_INET6)
aec3d242
 				LOG(L_ERR, "ERROR: sctp_bind_sock: might be caused by using a "
66b00f85
 							"link local address, try site local or global\n");
 		#endif
 			/* try to continue, a secondary address bind failure is not 
 			 * critical */
 		}
aec3d242
 	return 0;
 error:
 	return -1;
 }
 
 
 
 /* init, bind & start listening on the corresp. sctp socket
c999e6bb
    returns 0 on success, -1 on error */
aec3d242
 int sctp_init_sock(struct socket_info* sock_info)
 {
 	union sockaddr_union* addr;
 	
 	sock_info->proto=PROTO_SCTP;
 	addr=&sock_info->su;
 	if (sctp_init_su(sock_info)!=0)
 		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
 	/* recvmsg must block 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 */
6deb1c53
 	if (sctp_init_sock_opt_common(sock_info->socket, sock_info->address.af)!=0)
aec3d242
 		goto error;
 	/* 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 */
 	
 	if (sctp_bind_sock(sock_info)<0)
 		goto error;
c3611173
 	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;
 }
 
 
c999e6bb
 #define USE_SCTP_OO
 
 #ifdef USE_SCTP_OO
 
 /* init, bind & start listening on the corresp. sctp socket, using
    sctp one-to-one mode
    returns 0 on success, -1 on error */
 int sctp_init_sock_oo(struct socket_info* sock_info)
 {
 	union sockaddr_union* addr;
 	int optval;
 	
 	sock_info->proto=PROTO_SCTP;
 	addr=&sock_info->su;
 	if (sctp_init_su(sock_info)!=0)
 		goto error;
 	sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_STREAM, 
 								IPPROTO_SCTP);
 	if (sock_info->socket==-1){
 		LOG(L_ERR, "ERROR: sctp_init_sock_oo: socket: %s\n", strerror(errno));
 		goto error;
 	}
 	INFO("sctp:oo socket %d initialized (%p)\n", sock_info->socket, sock_info);
 	/* make socket non-blocking */
 	optval=fcntl(sock_info->socket, F_GETFL);
 	if (optval==-1){
 		LOG(L_ERR, "ERROR: sctp_init_sock_oo: 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: sctp_init_sock_oo: fcntl: set non-blocking failed:"
 				" (%d) %s\n", errno, strerror(errno));
 		goto error;
 	}
 	
 	/* set sock opts */
6deb1c53
 	if (sctp_init_sock_opt_common(sock_info->socket, sock_info->address.af)!=0)
c999e6bb
 		goto error;
 	
 #ifdef SCTP_REUSE_PORT
 	/* set reuse port */
 	optval=1;
 	if (setsockopt(sock_info->socket, IPPROTO_SCTP, SCTP_REUSE_PORT ,
 					(void*)&optval, sizeof(optval)) ==-1){
 		LOG(L_ERR, "ERROR: sctp_init_sock_oo: setsockopt: "
 					"SCTP_REUSE_PORT: %s\n", strerror(errno));
 		goto error;
 	}
 #endif /* SCTP_REUSE_PORT */
 	
 	if (sctp_bind_sock(sock_info)<0)
 		goto error;
 	if (listen(sock_info->socket, 1)<0){
 		LOG(L_ERR, "ERROR: sctp_init_sock_oo: listen(%x, 1) on %s: %s\n",
 					sock_info->socket, sock_info->address_str.s,
 					strerror(errno));
 		goto error;
 	}
 	return 0;
 error:
 	return -1;
 }
 
 #endif /* USE_SCTP_OO */
 
c3611173
 
31fd952b
 #ifdef SCTP_CONN_REUSE
 
 /* we  need SCTP_ADDR_HASH for being able to make inquires related to existing
    sctp association to a particular address  (optional) */
 /*#define SCTP_ADDR_HASH*/
 
 #define SCTP_ID_HASH_SIZE 1024 /* must be 2^k */
 #define SCTP_ASSOC_HASH_SIZE 1024 /* must be 2^k */
 #define SCTP_ADDR_HASH_SIZE 1024 /* must be 2^k */
 
 /* lock method */
 #ifdef GEN_LOCK_T_UNLIMITED
 #define SCTP_HASH_LOCK_PER_BUCKET
 #elif defined GEN_LOCK_SET_T_UNLIMITED
 #define SCTP_HASH_LOCK_SET
 #else
 #define SCTP_HASH_ONE_LOCK
 #endif
 
 
 #ifdef SCTP_HASH_LOCK_PER_BUCKET
 /* lock included in the hash bucket */
 #define LOCK_SCTP_ID_H(h)		lock_get(&sctp_con_id_hash[(h)].lock)
 #define UNLOCK_SCTP_ID_H(h)		lock_release(&sctp_con_id_hash[(h)].lock)
 #define LOCK_SCTP_ASSOC_H(h)	lock_get(&sctp_con_assoc_hash[(h)].lock)
 #define UNLOCK_SCTP_ASSOC_H(h)	lock_release(&sctp_con_assoc_hash[(h)].lock)
 #define LOCK_SCTP_ADDR_H(h)		lock_get(&sctp_con_addr_hash[(h)].lock)
 #define UNLOCK_SCTP_ADDR_H(h)	lock_release(&sctp_con_addr_hash[(h)].lock)
 #elif defined SCTP_HASH_LOCK_SET
 static gen_lock_set_t* sctp_con_id_h_lock_set=0;
 static gen_lock_set_t* sctp_con_assoc_h_lock_set=0;
 static gen_lock_set_t* sctp_con_addr_h_lock_set=0;
 #define LOCK_SCTP_ID_H(h)		lock_set_get(sctp_con_id_h_lock_set, (h))
 #define UNLOCK_SCTP_ID_H(h)		lock_set_release(sctp_con_id_h_lock_set, (h))
 #define LOCK_SCTP_ASSOC_H(h)	lock_set_get(sctp_con_assoc_h_lock_set, (h))
 #define UNLOCK_SCTP_ASSOC_H(h)	\
 	lock_set_release(sctp_con_assoc_h_lock_set, (h))
 #define LOCK_SCTP_ADDR_H(h)	lock_set_get(sctp_con_addr_h_lock_set, (h))
 #define UNLOCK_SCTP_ADDR_H(h)	lock_set_release(sctp_con_addr_h_lock_set, (h))
 #else /* use only one lock */
 static gen_lock_t* sctp_con_id_h_lock=0;
 static gen_lock_t* sctp_con_assoc_h_lock=0;
 static gen_lock_t* sctp_con_addr_h_lock=0;
 #define LOCK_SCTP_ID_H(h)		lock_get(sctp_con_id_h_lock)
 #define UNLOCK_SCTP_ID_H(h)		lock_release(sctp_con_id_hlock)
 #define LOCK_SCTP_ASSOC_H(h)	lock_get(sctp_con_assoc_h_lock)
 #define UNLOCK_SCTP_ASSOC_H(h)	lock_release(sctp_con_assoc_h_lock)
 #define LOCK_SCTP_ADDR_H(h)	lock_get(sctp_con_addr_h_lock)
 #define UNLOCK_SCTP_ADDR_H(h)	lock_release(sctp_con_addr_h_lock)
 #endif /* SCTP_HASH_LOCK_PER_BUCKET */
 
 
 /* sctp connection flags */
 #define SCTP_CON_UP_SEEN   1
 #define SCTP_CON_RCV_SEEN  2
 #define SCTP_CON_DOWN_SEEN 4
 
 struct sctp_connection{
 	unsigned int id;       /**< ser unique global id */
 	unsigned int assoc_id; /**< sctp assoc id (can be reused for new assocs)*/
 	struct socket_info* si; /**< local socket used */
 	unsigned flags; /**< internal flags UP_SEEN, RCV_SEEN, DOWN_SEEN */
 	ticks_t start;
 	ticks_t expire; 
 	union sockaddr_union remote; /**< remote ip & port */
 };
 
 struct sctp_lst_connector{
 	/* id hash */
 	struct sctp_con_elem* next_id;
 	struct sctp_con_elem* prev_id;
 	/* assoc hash */
 	struct sctp_con_elem* next_assoc;
 	struct sctp_con_elem* prev_assoc;
 #ifdef SCTP_ADDR_HASH
 	/* addr hash */
 	struct sctp_con_elem* next_addr;
 	struct sctp_con_elem* prev_addr;
 #endif /* SCTP_ADDR_HASH */
 };
 
 struct sctp_con_elem{
 	struct sctp_lst_connector l; /* must be first */
 	atomic_t refcnt;
 	/* data */
 	struct sctp_connection con;
 };
 
 struct sctp_con_id_hash_head{
 	struct sctp_lst_connector l; /* must be first */
 #ifdef SCTP_HASH_LOCK_PER_BUCKET
 	gen_lock_t lock;
 #endif /* SCTP_HASH_LOCK_PER_BUCKET */
 };
 
 struct sctp_con_assoc_hash_head{
 	struct sctp_lst_connector l; /* must be first */
 #ifdef SCTP_HASH_LOCK_PER_BUCKET
 	gen_lock_t lock;
 #endif /* SCTP_HASH_LOCK_PER_BUCKET */
 };
 
 #ifdef SCTP_ADDR_HASH
 struct sctp_con_addr_hash_head{
 	struct sctp_lst_connector l; /* must be first */
 #ifdef SCTP_HASH_LOCK_PER_BUCKET
 	gen_lock_t lock;
 #endif /* SCTP_HASH_LOCK_PER_BUCKET */
 };
 #endif /* SCTP_ADDR_HASH */
 
 static struct sctp_con_id_hash_head*     sctp_con_id_hash;
 static struct sctp_con_assoc_hash_head*  sctp_con_assoc_hash;
 #ifdef SCTP_ADDR_HASH
 static struct sctp_con_addr_hash_head*  sctp_con_addr_hash;
 #endif /* SCTP_ADDR_HASH */
 
 static atomic_t* sctp_id;
 static atomic_t* sctp_conn_tracked;
 
 
 #define get_sctp_con_id_hash(id) ((id) % SCTP_ID_HASH_SIZE)
 #define get_sctp_con_assoc_hash(assoc_id)  ((assoc_id) % SCTP_ASSOC_HASH_SIZE)
 #ifdef SCTP_ADDR_HASH
 static inline unsigned get_sctp_con_addr_hash(union sockaddr_union* remote,
 											struct socket_info* si)
 {
 	struct ip_addr ip;
 	unsigned short port;
 	unsigned h;
 	
 	su2ip_addr(&ip, remote);
 	port=su_getport(remote);
 	if (likely(ip.len==4))
 		h=ip.u.addr32[0]^port;
 	else if (ip.len==16)
 		h=ip.u.addr32[0]^ip.u.addr32[1]^ip.u.addr32[2]^ ip.u.addr32[3]^port;
 	else
 		h=0; /* error */
 	/* make sure the first bits are influenced by all 32
 	 * (the first log2(SCTP_ADDR_HASH_SIZE) bits should be a mix of all
 	 *  32)*/
 	h ^= h>>17;
 	h ^= h>>7;
 	return h & (SCTP_ADDR_HASH_SIZE-1);
 }
 #endif /* SCTP_ADDR_HASH */
 
 
 
 /** destroy sctp conn hashes. */
 void destroy_sctp_con_tracking()
 {
 	int r;
 	
 #ifdef SCTP_HASH_LOCK_PER_BUCKET
 	if (sctp_con_id_hash)
 		for(r=0; r<SCTP_ID_HASH_SIZE; r++)
 			lock_destroy(&sctp_con_id_hash[r].lock);
 	if (sctp_con_assoc_hash)
 		for(r=0; r<SCTP_ASSOC_HASH_SIZE; r++)
 			lock_destroy(&sctp_con_assoc_hash[r].lock);
 #	ifdef SCTP_ADDR_HASH
 	if (sctp_con_addr_hash)
 		for(r=0; r<SCTP_ADDR_HASH_SIZE; r++)
 			lock_destroy(&sctp_con_addr_hash[r].lock);
 #	endif /* SCTP_ADDR_HASH */
 #elif defined SCTP_HASH_LOCK_SET
 	if (sctp_con_id_h_lock_set){
 		lock_set_destroy(sctp_con_id_h_lock_set);
 		lock_set_dealloc(sctp_con_id_h_lock_set);
 		sctp_con_id_h_lock_set=0;
 	}
 	if (sctp_con_assoc_h_lock_set){
 		lock_set_destroy(sctp_con_assoc_h_lock_set);
 		lock_set_dealloc(sctp_con_assoc_h_lock_set);
 		sctp_con_assoc_h_lock_set=0;
 	}
 #	ifdef SCTP_ADDR_HASH
 	if (sctp_con_addr_h_lock_set){
 		lock_set_destroy(sctp_con_addr_h_lock_set);
 		lock_set_dealloc(sctp_con_addr_h_lock_set);
 		sctp_con_addr_h_lock_set=0;
 	}
 #	endif /* SCTP_ADDR_HASH */
 #else /* SCTP_HASH_ONE_LOCK */
 	if (sctp_con_id_h_lock){
 		lock_destroy(sctp_con_id_h_lock);
 		lock_dealloc(sctp_con_id_h_lock);
 		sctp_con_id_h_lock=0;
 	}
 	if (sctp_con_assoc_h_lock){
 		lock_destroy(sctp_con_assoc_h_lock);
 		lock_dealloc(sctp_con_assoc_h_lock);
 		sctp_con_assoc_h_lock=0;
 	}
 #	ifdef SCTP_ADDR_HASH
 	if (sctp_con_addr_h_lock){
 		lock_destroy(sctp_con_addr_h_lock);
 		lock_dealloc(sctp_con_addr_h_lock);
 		sctp_con_addr_h_lock=0;
 	}
 #	endif /* SCTP_ADDR_HASH */
 #endif /* SCTP_HASH_LOCK_PER_BUCKET/SCTP_HASH_LOCK_SET/one lock */
 	if (sctp_con_id_hash){
 		shm_free(sctp_con_id_hash);
 		sctp_con_id_hash=0;
 	}
 	if (sctp_con_assoc_hash){
 		shm_free(sctp_con_assoc_hash);
 		sctp_con_assoc_hash=0;
 	}
 #ifdef SCTP_ADDR_HASH
 	if (sctp_con_addr_hash){
 		shm_free(sctp_con_addr_hash);
 		sctp_con_addr_hash=0;
 	}
 #endif /* SCTP_ADDR_HASH */
 	if (sctp_id){
 		shm_free(sctp_id);
 		sctp_id=0;
 	}
 	if (sctp_conn_tracked){
 		shm_free(sctp_conn_tracked);
 		sctp_conn_tracked=0;
 	}
 }
 
 
 
 /** intializaze sctp_conn hashes.
   * @return 0 on success, <0 on error
   */
 int init_sctp_con_tracking()
 {
 	int r, ret;
 	
 	sctp_con_id_hash=shm_malloc(SCTP_ID_HASH_SIZE*sizeof(*sctp_con_id_hash));
 	sctp_con_assoc_hash=shm_malloc(SCTP_ASSOC_HASH_SIZE*
 									sizeof(*sctp_con_assoc_hash));
 #ifdef SCTP_ADDR_HASH
 	sctp_con_addr_hash=shm_malloc(SCTP_ADDR_HASH_SIZE*
 									sizeof(*sctp_con_addr_hash));
 #endif /* SCTP_ADDR_HASH */
 	sctp_id=shm_malloc(sizeof(*sctp_id));
 	sctp_conn_tracked=shm_malloc(sizeof(*sctp_conn_tracked));
 	if (sctp_con_id_hash==0 || sctp_con_assoc_hash==0 ||
 #ifdef SCTP_ADDR_HASH
 			sctp_con_addr_hash==0 ||
 #endif /* SCTP_ADDR_HASH */
 			sctp_id==0 || sctp_conn_tracked==0){
 		ERR("sctp init: memory allocation error\n");
 		ret=E_OUT_OF_MEM;
 		goto error;
 	}
 	atomic_set(sctp_id, 0);
 	atomic_set(sctp_conn_tracked, 0);
 	for (r=0; r<SCTP_ID_HASH_SIZE; r++)
 		clist_init(&sctp_con_id_hash[r], l.next_id, l.prev_id);
 	for (r=0; r<SCTP_ASSOC_HASH_SIZE; r++)
 		clist_init(&sctp_con_assoc_hash[r], l.next_assoc, l.prev_assoc);
 #ifdef SCTP_ADDR_HASH
 	for (r=0; r<SCTP_ADDR_HASH_SIZE; r++)
 		clist_init(&sctp_con_addr_hash[r], l.next_addr, l.prev_addr);
 #endif /* SCTP_ADDR_HASH */
 #ifdef SCTP_HASH_LOCK_PER_BUCKET
 	for (r=0; r<SCTP_ID_HASH_SIZE; r++){
 		if (lock_init(&sctp_con_id_hash[r].lock)==0){
 			ret=-1;
 			ERR("sctp init: failed to initialize locks\n");
 			goto error;
 		}
 	}
 	for (r=0; r<SCTP_ASSOC_HASH_SIZE; r++){
 		if (lock_init(&sctp_con_assoc_hash[r].lock)==0){
 			ret=-1;
 			ERR("sctp init: failed to initialize locks\n");
 			goto error;
 		}
 	}
 #	ifdef SCTP_ADDR_HASH
 	for (r=0; r<SCTP_ADDR_HASH_SIZE; r++){
 		if (lock_init(&sctp_con_addr_hash[r].lock)==0){
 			ret=-1;
 			ERR("sctp init: failed to initialize locks\n");
 			goto error;
 		}
 	}
 #	endif /* SCTP_ADDR_HASH */
 #elif defined SCTP_HASH_LOCK_SET
 	sctp_con_id_h_lock_set=lock_set_alloc(SCTP_ID_HASH_SIZE);
 	sctp_con_assoc_h_lock_set=lock_set_alloc(SCTP_ASSOC_HASH_SIZE);
 #	ifdef SCTP_ADDR_HASH
 	sctp_con_addr_h_lock_set=lock_set_alloc(SCTP_ADDR_HASH_SIZE);
 #	endif /* SCTP_ADDR_HASH */
 	if (sctp_con_id_h_lock_set==0 || sctp_con_assoc_h_lock_set==0
 #	ifdef SCTP_ADDR_HASH
 			|| sctp_con_addr_h_lock_set==0
 #	endif /* SCTP_ADDR_HASH */
 			){
 		ret=E_OUT_OF_MEM;
 		ERR("sctp_init: failed to alloc lock sets\n");
 		goto error;
 	}
 	if (lock_set_init(sctp_con_id_h_lock_set)==0){
 		lock_set_dealloc(sctp_con_id_h_lock_set);
 		sctp_con_id_h_lock_set=0;
 		ret=-1;
 		ERR("sctp init: failed to initialize lock set\n");
 		goto error;
 	}
 	if (lock_set_init(sctp_con_assoc_h_lock_set)==0){
 		lock_set_dealloc(sctp_con_assoc_h_lock_set);
 		sctp_con_assoc_h_lock_set=0;
 		ret=-1;
 		ERR("sctp init: failed to initialize lock set\n");
 		goto error;
 	}
 #	ifdef SCTP_ADDR_HASH
 	if (lock_set_init(sctp_con_addr_h_lock_set)==0){
 		lock_set_dealloc(sctp_con_addr_h_lock_set);
 		sctp_con_addr_h_lock_set=0;
 		ret=-1;
 		ERR("sctp init: failed to initialize lock set\n");
 		goto error;
 	}
 #	endif /* SCTP_ADDR_HASH */
 #else /* SCTP_HASH_ONE_LOCK */
 	sctp_con_id_h_lock=lock_alloc();
 	sctp_con_assoc_h_lock=lock_alloc();
 #	ifdef SCTP_ADDR_HASH
 	sctp_con_addr_h_lock=lock_alloc();
 #	endif /* SCTP_ADDR_HASH */
 	if (sctp_con_id_h_lock==0 || sctp_con_assoc_h_lock==0
 #	ifdef SCTP_ADDR_HASH
 			|| sctp_con_addr_h_lock==0
 #	endif /* SCTP_ADDR_HASH */
 			){
 		ret=E_OUT_OF_MEM;
 		ERR("sctp init: failed to alloc locks\n");
 		goto error;
 	}
 	if (lock_init(sctp_con_id_h_lock)==0){
 		lock_dealloc(sctp_con_id_h_lock);
 		sctp_con_id_h_lock=0;
 		ret=-1;
 		ERR("sctp init: failed to initialize lock\n");
 		goto error;
 	}
 	if (lock_init(sctp_con_assoc_h_lock)==0){
 		lock_dealloc(sctp_con_assoc_h_lock);
 		sctp_con_assoc_h_lock=0;
 		ret=-1;
 		ERR("sctp init: failed to initialize lock\n");
 		goto error;
 	}
 #	ifdef SCTP_ADDR_HASH
 	if (lock_init(sctp_con_addr_h_lock)==0){
 		lock_dealloc(sctp_con_addr_h_lock);
 		sctp_con_addr_h_lock=0;
 		ret=-1;
 		ERR("sctp init: failed to initialize lock\n");
 		goto error;
 	}
 #	endif /* SCTP_ADDR_HASH */
 #endif /* SCTP_HASH_LOCK_PER_BUCKET/SCTP_HASH_LOCK_SET/one lock */
 	return 0;
 error:
 	destroy_sctp_con_tracking();
 	return ret;
 }
 
 
 
 #if 0
 /** adds "e" to the hashes, safe locking version.*/
 static void sctp_con_add(struct sctp_con_elem* e)
 {
 	unsigned hash;
 	DBG("sctp_con_add(%p) ( ser id %d, assoc_id %d)\n",
 			e, e->con.id, e->con.assoc_id);
 	
 	e->l.next_id=e->l.prev_id=0;
 	e->l.next_assoc=e->l.prev_assoc=0;
 #ifdef SCTP_ADDR_HASH
 	e->l.next_addr=e->l.prev_addr=0;
 	e->refcnt.val+=3; /* account for the 3 lists */
 #else /* SCTP_ADDR_HASH */
 	e->refcnt.val+=2; /* account for the 2 lists */
 #endif /* SCTP_ADDR_HASH */
 	hash=get_sctp_con_id_hash(e->con.id);
 	DBG("adding to con id hash %d\n", hash);
 	LOCK_SCTP_ID_H(hash);
 		clist_insert(&sctp_con_id_hash[hash], e, l.next_id, l.prev_id);
 	UNLOCK_SCTP_ID_H(hash);
 	hash=get_sctp_con_assoc_hash(e->con.assoc_id);
 	DBG("adding to assoc_id hash %d\n", hash);
 	LOCK_SCTP_ASSOC_H(hash);
 		clist_insert(&sctp_con_assoc_hash[hash], e,
 						l.next_assoc, l.prev_assoc);
 	UNLOCK_SCTP_ASSOC_H(hash);
 #ifdef SCTP_ADDR_HASH
 	hash=get_sctp_con_addr_hash(&e->con.remote, e->con.si);
 	DBG("adding to addr hash %d\n", hash);
 	LOCK_SCTP_ADDR_H(hash);
 		clist_insert(&sctp_con_addr_hash[hash], e,
 						l.next_addr, l.prev_addr);
 	UNLOCK_SCTP_ADDR_H(hash);
 #endif /* SCTP_ADDR_HASH */
 	atomic_inc(sctp_conn_tracked);
 }
 #endif
 
 
 
 /** helper internal del elem function, the id hash must be locked.
   * WARNING: the id hash(h) _must_ be locked (LOCK_SCTP_ID_H(h)).
   * @param h - id hash
   * @param e - sctp_con_elem to delete (from all the hashes)
   * @return 0 if the id hash was unlocked, 1 if it's still locked */
 inline static int _sctp_con_del_id_locked(unsigned h, struct sctp_con_elem* e)
 {
 	unsigned assoc_id_h;
 	int deref; /* delayed de-reference counter */
 	int locked;
 #ifdef SCTP_ADDR_HASH
 	unsigned addr_h;
 #endif /* SCTP_ADDR_HASH */
 	
 	locked=1;
 	clist_rm(e, l.next_id, l.prev_id);
 	e->l.next_id=e->l.prev_id=0; /* mark it as id unhashed */
 	/* delay atomic dereference, so that we'll perform only one
 	   atomic op. even for multiple derefs. It also has the
 	   nice side-effect that the entry will be guaranteed to be
 	   referenced until we perform the delayed deref. at the end,
 	   so we don't need to keep some lock to prevent somebody from
 	   deleting the entry from under us */
 	deref=1; /* removed from one list =>  deref once */
 	/* remove it from the assoc hash if needed */
 	if (likely(e->l.next_assoc)){
 		UNLOCK_SCTP_ID_H(h);
 		locked=0; /* no longer id-locked */
 		/* we haven't dec. refcnt, so it's still safe to use e */
 		assoc_id_h=get_sctp_con_assoc_hash(e->con.assoc_id);
 		LOCK_SCTP_ASSOC_H(assoc_id_h);
 			/* make sure nobody removed it in the meantime */
 			if (likely(e->l.next_assoc)){
 				clist_rm(e, l.next_assoc, l.prev_assoc);
 				e->l.next_assoc=e->l.prev_assoc=0; /* mark it as removed */
 				deref++; /* rm'ed from the assoc list => inc. delayed deref. */
 			}
 		UNLOCK_SCTP_ASSOC_H(assoc_id_h);
 	}
 #ifdef SCTP_ADDR_HASH
 	/* remove it from the addr. hash if needed */
 	if (likely(e->l.next_addr)){
 		if (unlikely(locked)){
 			UNLOCK_SCTP_ID_H(h);
 			locked=0; /* no longer id-locked */
 		}
 		addr_h=get_sctp_con_addr_hash(&e->con.remote, e->con.si);
 		LOCK_SCTP_ADDR_H(addr_h);
 			/* make sure nobody removed it in the meantime */
 			if (likely(e->l.next_addr)){
 				clist_rm(e, l.next_addr, l.prev_addr);
 				e->l.next_addr=e->l.prev_addr=0; /* mark it as removed */
 				deref++; /* rm'ed from the addr list => inc. delayed deref. */
 			}
 		UNLOCK_SCTP_ADDR_H(addr_h);
 	}
 #endif /* SCTP_ADDR_HASH */
 	
 	/* performed delayed de-reference */
 	if (atomic_add(&e->refcnt, -deref)==0){
 		atomic_dec(sctp_conn_tracked);
 		shm_free(e);
 	}
 	else
 		DBG("del assoc post-deref (kept): ser id %d, assoc_id %d,"
 			" post-refcnt %d, deref %d, post-tracked %d\n",
 			e->con.id, e->con.assoc_id, atomic_get(&e->refcnt), deref,
 			atomic_get(sctp_conn_tracked));
 	return locked;
 }
 
 
 
 /** helper internal del elem function, the assoc hash must be locked.
   * WARNING: the assoc hash(h) _must_ be locked (LOCK_SCTP_ASSOC_H(h)).
   * @param h - assoc hash
   * @param e - sctp_con_elem to delete (from all the hashes)
   * @return 0 if the assoc hash was unlocked, 1 if it's still locked */
 inline static int _sctp_con_del_assoc_locked(unsigned h,
 												struct sctp_con_elem* e)
 {
 	unsigned id_hash;
 	int deref; /* delayed de-reference counter */
 	int locked;
 #ifdef SCTP_ADDR_HASH
 	unsigned addr_h;
 #endif /* SCTP_ADDR_HASH */
 	
 	locked=1;
 	clist_rm(e, l.next_assoc, l.prev_assoc);
 	e->l.next_assoc=e->l.prev_assoc=0; /* mark it as assoc unhashed */
 	/* delay atomic dereference, so that we'll perform only one
 	   atomic op. even for multiple derefs. It also has the
 	   nice side-effect that the entry will be guaranteed to be
 	   referenced until we perform the delayed deref. at the end,
 	   so we don't need to keep some lock to prevent somebody from
 	   deleting the entry from under us */
 	deref=1; /* removed from one list =>  deref once */
 	/* remove it from the id hash if needed */
 	if (likely(e->l.next_id)){
 		UNLOCK_SCTP_ASSOC_H(h);
 		locked=0; /* no longer assoc-hash-locked */
 		/* we have a ref. to it so it's still safe to use e */
 		id_hash=get_sctp_con_id_hash(e->con.id);
 		LOCK_SCTP_ID_H(id_hash);
 			/* make sure nobody removed it in the meantime */
 			if (likely(e->l.next_id)){
 				clist_rm(e, l.next_id, l.prev_id);
 				e->l.next_id=e->l.prev_id=0; /* mark it as removed */
 				deref++; /* rm'ed from the id list => inc. delayed deref. */
 			}
 		UNLOCK_SCTP_ID_H(id_hash);
 	}
 #ifdef SCTP_ADDR_HASH
 	/* remove it from the addr. hash if needed */
 	if (likely(e->l.next_addr)){
 		if (unlikely(locked)){
 			UNLOCK_SCTP_ASSOC_H(h);
 			locked=0; /* no longer id-locked */
 		}
 		addr_h=get_sctp_con_addr_hash(&e->con.remote, e->con.si);
 		LOCK_SCTP_ADDR_H(addr_h);
 			/* make sure nobody removed it in the meantime */
 			if (likely(e->l.next_addr)){
 				clist_rm(e, l.next_addr, l.prev_addr);
 				e->l.next_addr=e->l.prev_addr=0; /* mark it as removed */
 				deref++; /* rm'ed from the addr list => inc. delayed deref. */
 			}
 		UNLOCK_SCTP_ADDR_H(addr_h);
 	}
 #endif /* SCTP_ADDR_HASH */
 	if (atomic_add(&e->refcnt, -deref)==0){
 		atomic_dec(sctp_conn_tracked);
 		shm_free(e);
 	}
 	else
 		DBG("del assoc post-deref (kept): ser id %d, assoc_id %d,"
 				" post-refcnt %d, deref %d, post-tracked %d\n",
 				e->con.id, e->con.assoc_id, atomic_get(&e->refcnt), deref,
 				atomic_get(sctp_conn_tracked));
 	return locked;
 }
 
 
 
 #ifdef SCTP_ADDR_HASH
 /** helper internal del elem function, the addr hash must be locked.
   * WARNING: the addr hash(h) _must_ be locked (LOCK_SCTP_ADDR_H(h)).
   * @param h - addr hash
   * @param e - sctp_con_elem to delete (from all the hashes)
   * @return 0 if the addr hash was unlocked, 1 if it's still locked */
 inline static int _sctp_con_del_addr_locked(unsigned h,
 												struct sctp_con_elem* e)
 {
 	unsigned id_hash;
 	unsigned assoc_id_h;
 	int deref; /* delayed de-reference counter */
 	int locked;
 	
 	locked=1;
 	clist_rm(e, l.next_addr, l.prev_addr);
 	e->l.next_addr=e->l.prev_addr=0; /* mark it as addr unhashed */
 	/* delay atomic dereference, so that we'll perform only one
 	   atomic op. even for multiple derefs. It also has the
 	   nice side-effect that the entry will be guaranteed to be
 	   referenced until we perform the delayed deref. at the end,
 	   so we don't need to keep some lock to prevent somebody from
 	   deleting the entry from under us */
 	deref=1; /* removed from one list =>  deref once */
 	/* remove it from the id hash if needed */
 	if (likely(e->l.next_id)){
 		UNLOCK_SCTP_ADDR_H(h);
 		locked=0; /* no longer addr-hash-locked */
 		/* we have a ref. to it so it's still safe to use e */
 		id_hash=get_sctp_con_id_hash(e->con.id);
 		LOCK_SCTP_ID_H(id_hash);
 			/* make sure nobody removed it in the meantime */
 			if (likely(e->l.next_id)){
 				clist_rm(e, l.next_id, l.prev_id);
 				e->l.next_id=e->l.prev_id=0; /* mark it as removed */
 				deref++; /* rm'ed from the id list => inc. delayed deref. */
 			}
 		UNLOCK_SCTP_ID_H(id_hash);
 	}
 	/* remove it from the assoc hash if needed */
 	if (likely(e->l.next_assoc)){
 		if (locked){
 			UNLOCK_SCTP_ADDR_H(h);
 			locked=0; /* no longer addr-hash-locked */
 		}
 		/* we haven't dec. refcnt, so it's still safe to use e */
 		assoc_id_h=get_sctp_con_assoc_hash(e->con.assoc_id);
 		LOCK_SCTP_ASSOC_H(assoc_id_h);
 			/* make sure nobody removed it in the meantime */
 			if (likely(e->l.next_assoc)){
 				clist_rm(e, l.next_assoc, l.prev_assoc);
 				e->l.next_assoc=e->l.prev_assoc=0; /* mark it as removed */
 				deref++; /* rm'ed from the assoc list => inc. delayed deref. */
 			}
 		UNLOCK_SCTP_ASSOC_H(assoc_id_h);
 	}
 	if (atomic_add(&e->refcnt, -deref)==0){
 		atomic_dec(sctp_conn_tracked);
 		shm_free(e);
 	}
 	else
 		DBG("del assoc post-deref (kept): ser id %d, assoc_id %d,"
 				" post-refcnt %d, deref %d, post-tracked %d\n",
 				e->con.id, e->con.assoc_id, atomic_get(&e->refcnt), deref,
 				atomic_get(sctp_conn_tracked));
 	return locked;
 }
 #endif /* SCTP_ADDR_HASH */
 
 
 
709fb806
 /** delete all tracked associations entries.
  */
 void sctp_con_tracking_flush()
 {
 	unsigned h;
 	struct sctp_con_elem* e;
 	struct sctp_con_elem* tmp;
 	
 	for (h=0; h<SCTP_ID_HASH_SIZE; h++){
 again:
 		LOCK_SCTP_ID_H(h);
 			clist_foreach_safe(&sctp_con_id_hash[h], e, tmp, l.next_id) {
 				if (_sctp_con_del_id_locked(h, e)==0){
 					/* unlocked, need to lock again and restart the list */
 					goto again;
 				}
 			}
 		UNLOCK_SCTP_ID_H(h);
 	}
 }
 
 
 
31fd952b
 /** using id, get the corresponding sctp assoc & socket. 
  *  @param id - ser unique assoc id
  *  @param si  - result parameter, filled with the socket info on success
  *  @param remote - result parameter, filled with the address and port
  *  @param del - if 1 delete the entry,
  *  @return assoc_id (!=0) on success & sets si, 0 on not found
  * si and remote will not be touched on failure.
  *
  */
 int sctp_con_get_assoc(unsigned int id, struct socket_info** si, 
 								union sockaddr_union *remote, int del)
 {
 	unsigned h;
 	ticks_t now; 
 	struct sctp_con_elem* e;
 	struct sctp_con_elem* tmp;
 	int ret;
 	
 	ret=0;
 	now=get_ticks_raw();
 	h=get_sctp_con_id_hash(id);
 #if 0
 again:
 #endif
 	LOCK_SCTP_ID_H(h);
 		clist_foreach_safe(&sctp_con_id_hash[h], e, tmp, l.next_id){
 			if(e->con.id==id){
 				ret=e->con.assoc_id;
 				*si=e->con.si;
 				*remote=e->con.remote;
 				if (del){
 					if (_sctp_con_del_id_locked(h, e)==0)
 						goto skip_unlock;
 				}else
57f57a43
 					e->con.expire=now +
 								S_TO_TICKS(cfg_get(sctp, sctp_cfg, autoclose));
31fd952b
 				break;
 			}
 #if 0
 			else if (TICKS_LT(e->con.expire, now)){
 				WARN("sctp con: found expired assoc %d, id %d (%d s ago)\n",
 						e->con.assoc_id, e->con.id,
 						TICKS_TO_S(now-e->con.expire));
 				if (_sctp_con_del_id_locked(h, e)==0)
 					goto again; /* if unlocked need to restart the list */
 			}
 #endif
 		}
 	UNLOCK_SCTP_ID_H(h);
 skip_unlock:
 	return ret;
 }
 
 
 
 /** using the assoc_id, remote addr. & socket, get the corresp. internal id.
  *  @param assoc_id - sctp assoc id
  *  @param si  - socket on which the packet was received
  *  @param del - if 1 delete the entry,
  *  @return assoc_id (!=0) on success, 0 on not found
  */
 int sctp_con_get_id(unsigned int assoc_id, union sockaddr_union* remote,
 					struct socket_info* si, int del)
 {
 	unsigned h;
 	ticks_t now; 
 	struct sctp_con_elem* e;
 	struct sctp_con_elem* tmp;
 	int ret;
 	
 	ret=0;
 	now=get_ticks_raw();
 	h=get_sctp_con_assoc_hash(assoc_id);
 #if 0
 again:
 #endif
 	LOCK_SCTP_ASSOC_H(h);
 		clist_foreach_safe(&sctp_con_assoc_hash[h], e, tmp, l.next_assoc){
 			if(e->con.assoc_id==assoc_id && e->con.si==si &&
 					su_cmp(remote, &e->con.remote)){
 				ret=e->con.id;
 				if (del){
 					if (_sctp_con_del_assoc_locked(h, e)==0)
 						goto skip_unlock;
 				}else
57f57a43
 					e->con.expire=now +
 								S_TO_TICKS(cfg_get(sctp, sctp_cfg, autoclose));
31fd952b
 				break;
 			}
 #if 0
 			else if (TICKS_LT(e->con.expire, now)){
 				WARN("sctp con: found expired assoc %d, id %d (%d s ago)\n",
 						e->con.assoc_id, e->con.id,
 						TICKS_TO_S(now-e->con.expire));
 				if (_sctp_con_del_assoc_locked(h, e)==0)
 					goto again; /* if unlocked need to restart the list */
 			}
 #endif
 		}
 	UNLOCK_SCTP_ASSOC_H(h);
 skip_unlock:
 	return ret;
 }
 
 
 
 #ifdef SCTP_ADDR_HASH
 /** using the dest. & source socket, get the corresponding id and assoc_id 
  *  @param remote   - peer address & port
  *  @param si       - local source socket
  *  @param assoc_id - result, filled with the sctp assoc_id
  *  @param del - if 1 delete the entry,
  *  @return ser id (!=0) on success, 0 on not found
  */
 int sctp_con_addr_get_id_assoc(union sockaddr_union* remote,
 								struct socket_info* si,
 								int* assoc_id, int del)
 {
 	unsigned h;
 	ticks_t now; 
 	struct sctp_con_elem* e;
 	struct sctp_con_elem* tmp;
 	int ret;
 	
 	ret=0;
 	*assoc_id=0;
 	now=get_ticks_raw();
 	h=get_sctp_con_addr_hash(remote, si);
 again:
 	LOCK_SCTP_ADDR_H(h);
 		clist_foreach_safe(&sctp_con_addr_hash[h], e, tmp, l.next_addr){
 			if(su_cmp(remote, &e->con.remote) && e->con.si==si){
 				ret=e->con.id;
 				*assoc_id=e->con.assoc_id;
 				if (del){
 					if (_sctp_con_del_addr_locked(h, e)==0)
 						goto skip_unlock;
 				}else
57f57a43
 					e->con.expire=now +
 								S_TO_TICKS(cfg_get(sctp, sctp_cfg, autoclose));
31fd952b
 				break;
 			}
 #if 0
 			else if (TICKS_LT(e->con.expire, now)){
 				WARN("sctp con: found expired assoc %d, id %d (%d s ago)\n",
 						e->con.assoc_id, e->con.id,
 						TICKS_TO_S(now-e->con.expire));
 				if (_sctp_con_del_addr_locked(h, e)==0)
 					goto again; /* if unlocked need to restart the list */
 			}
 #endif
 		}
 	UNLOCK_SCTP_ADDR_H(h);
 skip_unlock:
 	return ret;
 }
 #endif /* SCTP_ADDR_HASH */
 
 
 
 /** del con tracking for (assod_id, si).
  * @return 0 on success, -1 on error (not found)
  */
 #define sctp_con_del_assoc(assoc_id, si) \
 	(-(sctp_con_get_id((assoc_id), (si), 1)==0))
 
 
 
 /** create a new sctp con elem.
   * @param id - ser connection id
   * @param assoc_id - sctp assoc id
   * @param si - corresp. socket
   * @param remote - remote side
   * @return pointer to shm allocated sctp_con_elem on success, 0 on error
   */
 struct sctp_con_elem* sctp_con_new(unsigned id, unsigned assoc_id, 
 									struct socket_info* si,
 									union sockaddr_union* remote)
 {
 	struct sctp_con_elem* e;
 	
 	e=shm_malloc(sizeof(*e));
 	if (unlikely(e==0))
 		goto error;
 	e->l.next_id=e->l.prev_id=0;
 	e->l.next_assoc=e->l.prev_assoc=0;
 	atomic_set(&e->refcnt, 0);
 	e->con.id=id;
 	e->con.assoc_id=assoc_id;
 	e->con.si=si;
 	e->con.flags=0;
 	if (likely(remote))
 		e->con.remote=*remote;
 	else
 		memset(&e->con.remote, 0, sizeof(e->con.remote));
 	e->con.start=get_ticks_raw();
57f57a43
 	e->con.expire=e->con.start +
 				S_TO_TICKS(cfg_get(sctp, sctp_cfg, autoclose));
31fd952b
 	return e;
 error:
 	return 0;
 }
 
 
 
 /** handles every ev on sctp assoc_id.
   * @return ser id on success (!=0) or 0 on not found/error
   */
 static int sctp_con_track(int assoc_id, struct socket_info* si,
 							union sockaddr_union* remote, int ev)
 {
 	int id;
 	unsigned hash;
 	unsigned assoc_hash;
 	struct sctp_con_elem* e;
 	struct sctp_con_elem* tmp;
 	
 	id=0;
 	DBG("sctp_con_track(%d, %p, %d) \n", assoc_id, si, ev);
 	
 	/* search for (assoc_id, si) */
 	assoc_hash=get_sctp_con_assoc_hash(assoc_id);
 	LOCK_SCTP_ASSOC_H(assoc_hash);
 		clist_foreach_safe(&sctp_con_assoc_hash[assoc_hash], e, tmp,
 								l.next_assoc){
 			/* we need to use the remote side address, because at least
 			   on linux assoc_id are immediately reused (even if sctp
 			   autoclose is off) and so it's possible that the association
 			   id we saved is already closed and assigned to another
 			   association by the time we search for it) */
 			if(e->con.assoc_id==assoc_id && e->con.si==si &&
 					su_cmp(remote, &e->con.remote)){
 				if (ev==SCTP_CON_DOWN_SEEN){
 					if (e->con.flags & SCTP_CON_UP_SEEN){
 						/* DOWN after UP => delete */
 						id=e->con.id;
 						/* do delete */
 						if (_sctp_con_del_assoc_locked(assoc_hash, e)==0)
 							goto found; /* skip unlock */
 					}else{
 						/* DOWN after DOWN => error
 						   DOWN after RCV w/ no UP -> not possible
 						    since we never create a tracking entry on RCV
 							only */
 						BUG("unexpected flags: %x for assoc_id %d, id %d"
 								", sctp con %p\n", e->con.flags, assoc_id,
 								e->con.id, e);
 						/* do delete */
 						if (_sctp_con_del_assoc_locked(assoc_hash, e)==0)
 							goto found; /* skip unlock */
 					}
 				}else if (ev==SCTP_CON_RCV_SEEN){
 					/* RCV after UP or DOWN => just mark RCV as seen */
 					id=e->con.id;
 					e->con.flags |= SCTP_CON_RCV_SEEN;
 				}else{
 					/* SCTP_CON_UP */
 					if (e->con.flags & SCTP_CON_DOWN_SEEN){
 						/* UP after DOWN => delete */
 						id=e->con.id;
 						/* do delete */
 						if (_sctp_con_del_assoc_locked(assoc_hash, e)==0)
 							goto found; /* skip unlock */
 					}else{
 						/* UP after UP or after RCVD => BUG */
 						BUG("connection with same assoc_id (%d) already"
 								" present, flags %x\n",
 								assoc_id, e->con.flags);
 					}
 				}
 				UNLOCK_SCTP_ASSOC_H(assoc_hash);
 				goto found;
 			}
 		}
 		/* not found */
 		if (unlikely(ev!=SCTP_CON_RCV_SEEN)){
 			/* UP or DOWN and no tracking entry => create new tracking entry
 			   for both of them (because we can have a re-ordered DOWN before
 			   the UP) */
 again:
 				id=atomic_add(sctp_id, 1);
 				if (unlikely(id==0)){
 					/* overflow  and 0 is not a valid id */
 					goto again;
 				}
 				e=sctp_con_new(id, assoc_id, si, remote);
 				if (likely(e)){
 					e->con.flags=ev;
 					e->l.next_id=e->l.prev_id=0;
 					e->l.next_assoc=e->l.prev_assoc=0;
 #ifdef SCTP_ADDR_HASH
 					e->l.next_addr=e->l.prev_addr=0;
 					e->refcnt.val+=3; /* account for the 3 lists */
 #else /* SCTP_ADDR_HASH */
 					e->refcnt.val+=2; /* account for the 2 lists */
 #endif /* SCTP_ADDR_HASH */
 					/* already locked */
 					clist_insert(&sctp_con_assoc_hash[assoc_hash], e,
 									l.next_assoc, l.prev_assoc);
 					hash=get_sctp_con_id_hash(e->con.id);
 					LOCK_SCTP_ID_H(hash);
 						clist_insert(&sctp_con_id_hash[hash], e,
 									l.next_id, l.prev_id);
 					UNLOCK_SCTP_ID_H(hash);
 #ifdef SCTP_ADDR_HASH
 					hash=get_sctp_con_addr_hash(&e->con.remote, e->con.si);
 					LOCK_SCTP_ADDR_H(hash);
 						clist_insert(&sctp_con_addr_hash[hash], e,
 									l.next_addr, l.prev_addr);
 					UNLOCK_SCTP_ADDR_H(hash);
 #endif /* SCTP_ADDR_HASH */
 					atomic_inc(sctp_conn_tracked);
 				}
 		} /* else not found and RCV -> ignore
 			 We cannot create a new entry because we don't know when to
 			 delete it (we can have UP DOWN RCV which would result in a
 			 tracking entry living forever). This means that if we receive
 			 a msg. on an assoc. before it's UP notification we won't know
 			 the id for connection reuse, but since happens very rarely it's
 			 an acceptable tradeoff */
 	UNLOCK_SCTP_ASSOC_H(assoc_hash);
 	if (unlikely(e==0)){
 		ERR("memory allocation failure\n");
 		goto error;
 	}
 found:
 	return id;
 error:
 	return 0;
 }
 
 
 
709fb806
 #else /* SCTP_CONN_REUSE */
 void sctp_con_tracking_flush() {}
31fd952b
 #endif /* SCTP_CONN_REUSE */
 
 
 int init_sctp()
 {
 	int ret;
 	
 	ret=0;
7a66235b
 	if (INIT_SCTP_STATS()!=0){
 		ERR("sctp init: failed to intialize sctp stats\n");
 		goto error;
 	}
31fd952b
 	/* sctp options must be initialized before  calling this function */
 	sctp_conn_no=shm_malloc(sizeof(*sctp_conn_tracked));
 	if ( sctp_conn_no==0){
 		ERR("sctp init: memory allocation error\n");
 		ret=E_OUT_OF_MEM;
 		goto error;
 	}
 	atomic_set(sctp_conn_no, 0);
 #ifdef SCTP_CONN_REUSE
 	return init_sctp_con_tracking();
 #endif
 error:
 	return ret;
 }
 
 
 
 void destroy_sctp()
 {
 	if (sctp_conn_no){
 		shm_free(sctp_conn_no);
 		sctp_conn_no=0;
 	}
 #ifdef SCTP_CONN_REUSE
 	destroy_sctp_con_tracking();
 #endif
7a66235b
 	DESTROY_SCTP_STATS();
31fd952b
 }
 
 
87d68ef9
 
92b6de79
 static int sctp_msg_send_ext(struct dest_info* dst, char* buf, unsigned len,
87d68ef9
 						struct sctp_sndrcvinfo* sndrcv_info);
92b6de79
 #define SCTP_SEND_FIRST_ASSOCID 1  /* sctp_raw_send flag */
 static int sctp_raw_send(int socket, char* buf, unsigned len,
 						union sockaddr_union* to,
 						struct sctp_sndrcvinfo* sndrcv_info,
 						int flags);
87d68ef9
 
 
 
09bef324
 /* debugging: return a string name for SCTP_ASSOC_CHANGE state */
 static char* sctp_assoc_change_state2s(short int state)
 {
 	char* s;
 	
 	switch(state){
 		case SCTP_COMM_UP:
 			s="SCTP_COMM_UP";
 			break;
 		case SCTP_COMM_LOST:
 			s="SCTP_COMM_LOST";
 			break;
 		case SCTP_RESTART:
 			s="SCTP_RESTART";
 			break;
 		case SCTP_SHUTDOWN_COMP:
 			s="SCTP_SHUTDOWN_COMP";
 			break;
 		case SCTP_CANT_STR_ASSOC:
 			s="SCTP_CANT_STR_ASSOC";
 			break;
 		default:
fedf3b94
 			s="UNKNOWN";
09bef324
 			break;
 	};
 	return s;
 }
 
 
 
 /* debugging: return a string name for a SCTP_PEER_ADDR_CHANGE state */
 static char* sctp_paddr_change_state2s(unsigned int state)
 {
 	char* s;
 	
 	switch (state){
 		case SCTP_ADDR_AVAILABLE:
 			s="SCTP_ADDR_AVAILABLE";
 			break;
 		case SCTP_ADDR_UNREACHABLE:
 			s="SCTP_ADDR_UNREACHABLE";
 			break;
 		case SCTP_ADDR_REMOVED:
 			s="SCTP_ADDR_REMOVED";
 			break;
 		case SCTP_ADDR_ADDED:
 			s="SCTP_ADDR_ADDED";
 			break;
 		case SCTP_ADDR_MADE_PRIM:
 			s="SCTP_ADDR_MADE_PRIM";
 			break;
602a2344
 	/* not supported by lksctp 1.0.6 
09bef324
 		case SCTP_ADDR_CONFIRMED:
 			s="SCTP_ADDR_CONFIRMED";
 			break;
602a2344
 	*/
09bef324
 		default:
 			s="UNKNOWN";
 			break;
 	}
 	return s;
 }
 
 
 
87d68ef9
 /* handle SCTP_SEND_FAILED notifications: if packet marked for retries
31fd952b
  * retry the send (with 0 assoc_id)
87d68ef9
  * returns 0 on success, -1 on failure
  */
 static int sctp_handle_send_failed(struct socket_info* si,
 									union sockaddr_union* su,
 									char* buf, unsigned len)
 {
 	union sctp_notification* snp;
 	struct sctp_sndrcvinfo sinfo;
 	struct dest_info dst;
 	char* data;
 	unsigned data_len;
 	int retries;
 	int ret;
57f57a43
 #ifdef HAVE_SCTP_SNDRCVINFO_PR_POLICY
 	int send_ttl;
 #endif
87d68ef9
 	
 	ret=-1;
7a66235b
 	SCTP_STATS_SEND_FAILED();
87d68ef9
 	snp=(union sctp_notification*) buf;
 	retries=snp->sn_send_failed.ssf_info.sinfo_context;
 	
 	/* don't retry on explicit remote error
 	 * (unfortunately we can't be more picky than this, we get no 
 	 * indication in the SEND_FAILED notification for other error
 	 * reasons (e.g. ABORT received, INIT timeout a.s.o)
 	 */
 	if (retries && (snp->sn_send_failed.ssf_error==0)) {
 		DBG("sctp: RETRY-ing (%d)\n", retries);
7a66235b
 		SCTP_STATS_SEND_FORCE_RETRY();
87d68ef9
 		retries--;
 		data=(char*)snp->sn_send_failed.ssf_data;
 		data_len=snp->sn_send_failed.ssf_length - 
 					sizeof(struct sctp_send_failed);
 		
 		memset(&sinfo, 0, sizeof(sinfo));
 		sinfo.sinfo_flags=SCTP_UNORDERED;
 #ifdef HAVE_SCTP_SNDRCVINFO_PR_POLICY
57f57a43
 		if ((send_ttl=cfg_get(sctp, sctp_cfg, send_ttl))){
87d68ef9
 			sinfo.sinfo_pr_policy=SCTP_PR_SCTP_TTL;
57f57a43
 			sinfo.sinfo_pr_value=send_ttl;
87d68ef9
 		}else
 			sinfo.info_pr_policy=SCTP_PR_SCTP_NONE;
 #else
57f57a43
 		sinfo.sinfo_timetolive=cfg_get(sctp, sctp_cfg, send_ttl);
87d68ef9
 #endif
 		sinfo.sinfo_context=retries;
 		
 		dst.to=*su;
 		dst.send_sock=si;
 		dst.id=0;
 		dst.proto=PROTO_SCTP;
 #ifdef USE_COMP
 		dst.comp=COMP_NONE;
 #endif
 		
92b6de79
 		ret=sctp_msg_send_ext(&dst, data, data_len, &sinfo);
87d68ef9
 	}
047b1dfb
 #ifdef USE_DST_BLACKLIST
5d6752dc
 	 else if (cfg_get(sctp, sctp_cfg, send_retries)) {
047b1dfb
 		/* blacklist only if send_retries is on, if off we blacklist
 		   from SCTP_ASSOC_CHANGE: SCTP_COMM_LOST/SCTP_CANT_STR_ASSOC
 		   which is better (because we can tell connect errors from send
 		   errors and we blacklist a failed dst only once) */
5d6752dc
 		dst_blacklist_su(BLST_ERR_SEND, PROTO_SCTP, su, 0, 0);
047b1dfb
 	}
 #endif /* USE_DST_BLACKLIST */
87d68ef9
 	
 	return (ret>0)?0:ret;
 }
 
 
 
7a6f43c8
 /* handle SCTP_ASOC_CHANGE notifications: map ser global sctp ids
  * to kernel asoc_ids. The global ids are needed because the kernel ones
  * might get reused after a close and so they are not unique for ser's
  * lifetime. We need a unique id to match replies to the association on
  * which we received the corresponding request (so that we can send them
  * back on the same asoc & socket if still opened).
  * returns 0 on success, -1 on failure
  */
 static int sctp_handle_assoc_change(struct socket_info* si,
 									union sockaddr_union* su,
92b6de79
 									union sctp_notification* snp
 									)
7a6f43c8
 {
31fd952b
 	int ret;
92b6de79
 	int state;
 	int assoc_id;
 	struct sctp_sndrcvinfo sinfo;
 	struct ip_addr ip; /* used only on error, for debugging */
 	
 	state=snp->sn_assoc_change.sac_state;
 	assoc_id=snp->sn_assoc_change.sac_assoc_id;
31fd952b
 	
 	ret=-1;
 	switch(state){
 		case SCTP_COMM_UP:
7a66235b
 			SCTP_STATS_ESTABLISHED();
31fd952b
 			atomic_inc(sctp_conn_no);
 #ifdef SCTP_CONN_REUSE
 			/* new connection, track it */
709fb806
 			if (likely(cfg_get(sctp, sctp_cfg, assoc_tracking)))
 					sctp_con_track(assoc_id, si, su, SCTP_CON_UP_SEEN);
31fd952b
 #if 0
 again:
 			id=atomic_add(sctp_id, 1);
 			if (unlikely(id==0)){
 				/* overflow  and 0 is not a valid id */
 				goto again;
 			}
 			e=sctp_con_new(id, assoc_id, si, su);
 			if (unlikely(e==0)){
 				ERR("memory allocation failure\n");
 			}else{
 				sctp_con_add(e);
 				ret=0;
 			}
 #endif
 #endif /* SCTP_CONN_REUSE */
92b6de79
 			if (unlikely((unsigned)atomic_get(sctp_conn_no) >
 							(unsigned)cfg_get(sctp, sctp_cfg, max_assocs))){
 				/* maximum assoc exceeded => we'll have to immediately 
 				   close it */
 				memset(&sinfo, 0, sizeof(sinfo));
 				sinfo.sinfo_flags=SCTP_UNORDERED | SCTP_ABORT;
 				sinfo.sinfo_assoc_id=assoc_id;
 				ret=sctp_raw_send(si->socket, ABORT_REASON_MAX_ASSOCS,
 											sizeof(ABORT_REASON_MAX_ASSOCS)-1,
 											su, &sinfo, 0);
 				if (ret<0){
 					su2ip_addr(&ip, su);
 					WARN("failed to ABORT new sctp association %d (%s:%d):"
 							" %s (%d)\n", assoc_id, ip_addr2a(&ip),
 							su_getport(su), strerror(errno), errno);
d4ff27b7
 				}else{
 					SCTP_STATS_LOCAL_REJECT();
92b6de79
 				}
 			}
31fd952b
 			break;
 		case SCTP_COMM_LOST:
7a66235b
 			SCTP_STATS_COMM_LOST();
31fd952b
 #ifdef USE_DST_BLACKLIST
 			/* blacklist only if send_retries is turned off (if on we don't
 			   know here if we did retry or we are at the first error) */
5d6752dc
 			if (cfg_get(sctp, sctp_cfg, send_retries)==0)
 						dst_blacklist_su(BLST_ERR_SEND, PROTO_SCTP, su, 0, 0);
31fd952b
 #endif /* USE_DST_BLACKLIST */
 			/* no break */
642ff762
 			goto comm_lost_cont;	/* do not increment counters for
 									   SCTP_SHUTDOWN_COMP */
31fd952b
 		case SCTP_SHUTDOWN_COMP:
8dfdb3c7
 			SCTP_STATS_ASSOC_SHUTDOWN();
642ff762
 comm_lost_cont:
31fd952b
 			atomic_dec(sctp_conn_no);
 #ifdef SCTP_CONN_REUSE
 			/* connection down*/
709fb806
 			if (likely(cfg_get(sctp, sctp_cfg, assoc_tracking)))
 				sctp_con_track(assoc_id, si, su, SCTP_CON_DOWN_SEEN);
31fd952b
 #if 0
 			if (unlikely(sctp_con_del_assoc(assoc_id, si)!=0))
 				WARN("sctp con: tried to remove inexistent connection\n");
 			else
 				ret=0;
 #endif
 #endif /* SCTP_CONN_REUSE */
 			break;
 		case SCTP_RESTART:
 			/* do nothing on restart */
 			break;
 		case SCTP_CANT_STR_ASSOC:
7a66235b
 			SCTP_STATS_CONNECT_FAILED();
31fd952b
 			/* do nothing when failing to start an assoc
 			  (in this case we never see SCTP_COMM_UP so we never 
 			  track the assoc) */
 #ifdef USE_DST_BLACKLIST
 			/* blacklist only if send_retries is turned off (if on we don't 
 			   know here if we did retry or we are at the first error) */
5d6752dc
 			if (cfg_get(sctp, sctp_cfg, send_retries)==0)
 					dst_blacklist_su(BLST_ERR_CONNECT, PROTO_SCTP, su, 0, 0);
31fd952b
 #endif /* USE_DST_BLACKLIST */
 			break;
 		default:
 			break;
 	}
 	return ret;
7a6f43c8
 }
 
 
 
4a4dd08f
 static int sctp_handle_notification(struct socket_info* si,
 									union sockaddr_union* su,
 									char* buf, unsigned len)
 {
 	union sctp_notification* snp;
09bef324
 	char su_buf[SU2A_MAX_STR_SIZE];
 	
 	#define SNOT DBG
 	#define ERR_LEN_TOO_SMALL(length, val, bind_addr, from_su, text) \
 		if (unlikely((length)<(val))){\
 			SNOT("ERROR: sctp notification from %s on %.*s:%d: " \
 						text " too short (%d bytes instead of %d bytes)\n", \
 						su2a((from_su), sizeof(*(from_su))), \
 						(bind_addr)->name.len, (bind_addr)->name.s, \
047b1dfb
 						(bind_addr)->port_no, (int)(length), (int)(val)); \
09bef324
 			goto error; \
 		}
4a4dd08f
 
 	if (len < sizeof(snp->sn_header)){
 		LOG(L_ERR, "ERROR: sctp_handle_notification: invalid length %d "
 					"on %.*s:%d, from %s\n",
 					len, si->name.len, si->name.s, si->port_no,
 					su2a(su, sizeof(*su)));
09bef324
 		goto error;
4a4dd08f
 	}
 	snp=(union sctp_notification*) buf;
 	switch(snp->sn_header.sn_type){
 		case SCTP_REMOTE_ERROR:
09bef324
 			ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_remote_error), si, su,
 								"SCTP_REMOTE_ERROR");
e11d7294
 			SCTP_EV_REMOTE_ERROR(&si->address, si->port_no, su, 
 									ntohs(snp->sn_remote_error.sre_error) );
09bef324
 			SNOT("sctp notification from %s on %.*s:%d: SCTP_REMOTE_ERROR:"
31fd952b
 					" %d, len %d\n, assoc_id %d",
09bef324
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no,
 					ntohs(snp->sn_remote_error.sre_error),
 					ntohs(snp->sn_remote_error.sre_length),
 					snp->sn_remote_error.sre_assoc_id
 				);
4a4dd08f
 			break;
 		case SCTP_SEND_FAILED:
09bef324
 			ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_send_failed), si, su,
 								"SCTP_SEND_FAILED");
e11d7294
 			SCTP_EV_SEND_FAILED(&si->address, si->port_no, su, 
 									snp->sn_send_failed.ssf_error);
09bef324
 			SNOT("sctp notification from %s on %.*s:%d: SCTP_SEND_FAILED:"
31fd952b
 					" error %d, assoc_id %d, flags %x\n",
09bef324
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no, snp->sn_send_failed.ssf_error,
 					snp->sn_send_failed.ssf_assoc_id,
 					snp->sn_send_failed.ssf_flags);
87d68ef9
 			sctp_handle_send_failed(si, su, buf, len);
09bef324
 			break;
 		case SCTP_PEER_ADDR_CHANGE:
 			ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_paddr_change), si, su,
 								"SCTP_PEER_ADDR_CHANGE");
e11d7294
 			SCTP_EV_PEER_ADDR_CHANGE(&si->address, si->port_no, su, 
 					sctp_paddr_change_state2s(snp->sn_paddr_change.spc_state),
 					snp->sn_paddr_change.spc_state,
 					&snp->sn_paddr_change.spc_aaddr);
09bef324
 			strcpy(su_buf, su2a((union sockaddr_union*)
 									&snp->sn_paddr_change.spc_aaddr, 
 									sizeof(snp->sn_paddr_change.spc_aaddr)));
 			SNOT("sctp notification from %s on %.*s:%d: SCTP_PEER_ADDR_CHANGE"
31fd952b
 					": %s: %s: assoc_id %d \n",
09bef324
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no, su_buf,
 					sctp_paddr_change_state2s(snp->sn_paddr_change.spc_state),
 					snp->sn_paddr_change.spc_assoc_id
 					);
4a4dd08f
 			break;
09bef324
 		case SCTP_SHUTDOWN_EVENT:
7a66235b
 			SCTP_STATS_REMOTE_SHUTDOWN();
09bef324
 			ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_shutdown_event), si, su,
 								"SCTP_SHUTDOWN_EVENT");
e11d7294
 			SCTP_EV_SHUTDOWN_EVENT(&si->address, si->port_no, su);
09bef324
 			SNOT("sctp notification from %s on %.*s:%d: SCTP_SHUTDOWN_EVENT:"
31fd952b
 					" assoc_id %d\n",
09bef324
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no, snp->sn_shutdown_event.sse_assoc_id);
 			break;
 		case SCTP_ASSOC_CHANGE:
 			ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_assoc_change), si, su,
 								"SCTP_ASSOC_CHANGE");
e11d7294
 			SCTP_EV_ASSOC_CHANGE(&si->address, si->port_no, su, 
 					sctp_assoc_change_state2s(snp->sn_assoc_change.sac_state),
 					snp->sn_assoc_change.sac_state);
09bef324
 			SNOT("sctp notification from %s on %.*s:%d: SCTP_ASSOC_CHANGE"
31fd952b
 					": %s: assoc_id %d, ostreams %d, istreams %d\n",
09bef324
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no,
 					sctp_assoc_change_state2s(snp->sn_assoc_change.sac_state),
 					snp->sn_assoc_change.sac_assoc_id,
 					snp->sn_assoc_change.sac_outbound_streams,
 					snp->sn_assoc_change.sac_inbound_streams
 					);
92b6de79
 			sctp_handle_assoc_change(si, su, snp);
09bef324
 			break;
 #ifdef SCTP_ADAPTION_INDICATION
 		case SCTP_ADAPTION_INDICATION:
 			ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_adaption_event), si, su,
 								"SCTP_ADAPTION_INDICATION");
 			SNOT("sctp notification from %s on %.*s:%d: "
 					"SCTP_ADAPTION_INDICATION \n",
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no);
 			break;
 #endif /* SCTP_ADAPTION_INDICATION */
 		case SCTP_PARTIAL_DELIVERY_EVENT:
 			ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_pdapi_event), si, su,
 								"SCTP_PARTIAL_DELIVERY_EVENT");
31fd952b
 			ERR("sctp notification from %s on %.*s:%d: "
 					"SCTP_PARTIAL_DELIVERY_EVENT not supported: %d %s,"
 					"assoc_id %d\n",
09bef324
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no, snp->sn_pdapi_event.pdapi_indication,
 					(snp->sn_pdapi_event.pdapi_indication==
 					 	SCTP_PARTIAL_DELIVERY_ABORTED)? " PD ABORTED":"",
 					snp->sn_pdapi_event.pdapi_assoc_id);
 			break;
 #ifdef SCTP_SENDER_DRY_EVENT /* new, not yet supported */
 		case SCTP_SENDER_DRY_EVENT:
 			ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_sender_dry_event),
 								si, su, "SCTP_SENDER_DRY_EVENT");
e11d7294
 			SCTP_EV_REMOTE_ERROR(&si->address, si->port_no, su);
09bef324
 			SNOT("sctp notification from %s on %.*s:%d: "
 					"SCTP_SENDER_DRY_EVENT on %d\n",
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no, snp->sn_sender_dry_event.sender_dry_assoc_id);
 			break;
 #endif /* SCTP_SENDER_DRY_EVENT */
4a4dd08f
 		default:
09bef324
 			SNOT("sctp notification from %s on %.*s:%d: UNKNOWN (%d)\n",
4a4dd08f
 					su2a(su, sizeof(*su)), si->name.len, si->name.s,
 					si->port_no, snp->sn_header.sn_type);
 	}
 	return 0;
09bef324
 error:
4a4dd08f
 	return -1;
09bef324
 	#undef ERR_LEN_TOO_SMALL
4a4dd08f
 }
 
 
 
c3611173
 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];
4a4dd08f
 	struct cmsghdr* cmsg;
 	/* use a larger buffer then needed in case some other ancillary info
 	 * is enabled */
 	char cbuf[CMSG_SPACE(sizeof(*sinfo))+CMSG_SPACE(1024)];
c3611173
 
 	
 	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;
31fd952b
 	ri.proto_reserved2=0;
c3611173
 	
 	iov[0].iov_base=buf;
 	iov[0].iov_len=BUF_SIZE;
 	msg.msg_iov=iov;
 	msg.msg_iovlen=1;
 	msg.msg_flags=0;
 	
 
 	/* initialize the config framework */
 	if (cfg_child_init()) goto error;
 	
 	for(;;){
 		msg.msg_name=&ri.src_su.s;
 		msg.msg_namelen=sockaddru_len(bind_address->su);
dd7c917c
 		msg.msg_control=cbuf;
 		msg.msg_controllen=sizeof(cbuf);
31fd952b
 		sinfo=0;
c3611173
 
4a4dd08f
 		len=recvmsg(bind_address->socket, &msg, 0);
c3611173
 		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;
 		}
5813703a
 		/* update the local config */
 		cfg_update();
 		
c3611173
 		if (unlikely(msg.msg_flags & MSG_NOTIFICATION)){
31fd952b
 			/* intercept useful notifications */
4a4dd08f
 			sctp_handle_notification(bind_address, &ri.src_su, buf, len);
c3611173
 			continue;
 		}else if (unlikely(!(msg.msg_flags & MSG_EOR))){
 			LOG(L_ERR, "ERROR: sctp_rcv_loop: partial delivery not"
 						"supported\n");
 			continue;
 		}
4a4dd08f
 		
c3611173
 		su2ip_addr(&ri.src_ip, &ri.src_su);
 		ri.src_port=su_getport(&ri.src_su);
4a4dd08f
 		
 		/* get ancillary data */
 		for (cmsg=CMSG_FIRSTHDR(&msg); cmsg; cmsg=CMSG_NXTHDR(&msg, cmsg)){
b7ef1576
 #ifdef SCTP_EXT
4a4dd08f
 			if (likely((cmsg->cmsg_level==IPPROTO_SCTP) &&
 						((cmsg->cmsg_type==SCTP_SNDRCV)
 						 || (cmsg->cmsg_type==SCTP_EXTRCV)
b7ef1576
 						) && (cmsg->cmsg_len>=CMSG_LEN(sizeof(*sinfo)))) )
 #else  /* !SCTP_EXT -- same as above but w/o SCTP_EXTRCV */
 			if (likely((cmsg->cmsg_level==IPPROTO_SCTP) &&
 						((cmsg->cmsg_type==SCTP_SNDRCV)
 						) && (cmsg->cmsg_len>=CMSG_LEN(sizeof(*sinfo)))) )
 #endif /*SCTP_EXT */
 			{
4a4dd08f
 				sinfo=(struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
 				DBG("sctp recv: message from %s:%d stream %d  ppid %x"
31fd952b
 						" flags %x%s tsn %u" " cumtsn %u assoc_id %d\n",
 						ip_addr2a(&ri.src_ip), ri.src_port,
4a4dd08f
 						sinfo->sinfo_stream, sinfo->sinfo_ppid,
 						sinfo->sinfo_flags,
dd7c917c
 						(sinfo->sinfo_flags&SCTP_UNORDERED)?
 							" (SCTP_UNORDERED)":"",
4a4dd08f
 						sinfo->sinfo_tsn, sinfo->sinfo_cumtsn, 
 						sinfo->sinfo_assoc_id);
 				break;
 			}
 		}
 		/* we  0-term the messages for debugging */
 		buf[len]=0; /* no need to save the previous char */
c3611173
 
 		/* sanity checks */
 		if (len<MIN_SCTP_PACKET) {
 			tmp=ip_addr2a(&ri.src_ip);
31fd952b
 			DBG("sctp_rcv_loop: probing packet received from %s:%d\n",
 					tmp, ri.src_port);
c3611173
 			continue;
 		}
 		if (ri.src_port==0){
 			tmp=ip_addr2a(&ri.src_ip);
31fd952b
 			LOG(L_INFO, "sctp_rcv_loop: dropping 0 port packet from %s:0\n",
c3611173
 						tmp);
 			continue;
 		}
a7c9b3d1
 #ifdef USE_COMP
 		ri.comp=COMP_NONE;
 #endif
31fd952b
 #ifdef SCTP_CONN_REUSE
709fb806
 		if (likely(cfg_get(sctp, sctp_cfg, assoc_tracking) && sinfo)){
31fd952b
 			ri.proto_reserved1 = sctp_con_track(sinfo->sinfo_assoc_id,
 												ri.bind_address, 
 												&ri.src_su,
 												SCTP_CON_RCV_SEEN);
 			/* debugging */
 			if (unlikely(ri.proto_reserved1==0))
 				DBG("no tracked assoc. found for assoc_id %d, from %s\n",
 						sinfo->sinfo_assoc_id, 
 						su2a(&ri.src_su, sizeof(ri.src_su)));
 #if 0
 			ri.proto_reserved1=
 				sctp_con_get_id(sinfo->sinfo_assoc_id, ri.bind_address, 0);
 #endif
 		}else
 			ri.proto_reserved1=0;
 #else /* SCTP_CONN_REUSE */
 		ri.proto_received1=0;
 #endif /* SCTP_CONN_REUSE */
c3611173
 		receive_msg(buf, len, &ri);
 	}
 error:
 	return -1;
 }
 
 
31fd952b
 
92b6de79
 /** low level sctp non-blocking send.
  * @param socket - sctp socket to send on.
  * @param buf   - data.
  * @param len   - lenght of the data.
  * @param to    - destination in ser sockaddr_union format.
  * @param sndrcv_info - sctp_sndrcvinfo structure pointer, pre-filled.
  * @param flags - can have one of the following values (or'ed):
  *                SCTP_SEND_FIRST_ASSOCID - try to send first to assoc_id
  *                and only if that fails use "to".
  * @return the numbers of bytes sent on success (>=0) and -1 on error.
  * On error errno is set too.
  */
 static int sctp_raw_send(int socket, char* buf, unsigned len,
 						union sockaddr_union* to,
 						struct sctp_sndrcvinfo* sndrcv_info,
 						int flags)
 {
 	int n;
 	int tolen;
 	int try_assoc_id;
 #if 0
 	struct ip_addr ip; /* used only on error, for debugging */
 #endif
 	struct msghdr msg;
 	struct iovec iov[1];
 	struct sctp_sndrcvinfo* sinfo;
 	struct cmsghdr* cmsg;
 	/* make sure msg_control will point to properly aligned data */
 	union {
 		struct cmsghdr cm;
 		char cbuf[CMSG_SPACE(sizeof(*sinfo))];
 	}ctrl_un;
 	
 	iov[0].iov_base=buf;
 	iov[0].iov_len=len;
 	msg.msg_iov=iov;
 	msg.msg_iovlen=1;
 	msg.msg_flags=0; /* not used on send (use instead sinfo_flags) */
 	msg.msg_control=ctrl_un.cbuf;
 	msg.msg_controllen=sizeof(ctrl_un.cbuf);
 	cmsg=CMSG_FIRSTHDR(&msg);
 	cmsg->cmsg_level=IPPROTO_SCTP;
 	cmsg->cmsg_type=SCTP_SNDRCV;
 	cmsg->cmsg_len=CMSG_LEN(sizeof(*sinfo));
 	sinfo=(struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
 	*sinfo=*sndrcv_info;
 	/* some systems need msg_controllen set to the actual size and not
 	 * something bigger (e.g. openbsd) */
 	msg.msg_controllen=cmsg->cmsg_len;
 	try_assoc_id= ((flags & SCTP_SEND_FIRST_ASSOCID) && sinfo->sinfo_assoc_id);
 	/* if assoc_id is set it means we want to send on association assoc_id
 	   and only if it's not opened any longer use the addresses */
 	if (try_assoc_id){
 		/* on linux msg->name has priority over assoc_id. To try first assoc_id
 		 * and then "to", one has to call first sendmsg() with msg->name==0 and
 		 * sinfo->assoc_id set. If it returns EPIPE => association is no longer
 		 * open => call again sendmsg() this time with msg->name!=0.
 		 * on freebsd assoc_id has priority over msg->name and moreover the
 		 * send falls back automatically to the address if the assoc_id is
 		 * closed, so a single call to sendmsg(msg->name, sinfo->assoc_id ) is
 		 * enough.  If one tries calling with msg->name==0 and the association
 		 * is no longer open send will return ENOENT.
 		 * on solaris it seems one must always use a dst address (assoc_id
 		 * will be ignored).
 		 */
 #ifdef __OS_linux
 		msg.msg_name=0;
 		msg.msg_namelen=0;
 #elif defined __OS_freebsd
 		tolen=sockaddru_len(*to);
 		msg.msg_name=&to->s;
 		msg.msg_namelen=tolen;
 #else /* __OS_* */
 		/* fallback for solaris and others, sent back to
 		  the address recorded (not exactly what we want, but there's
 		  no way to fallback to "to") */
 		tolen=sockaddru_len(*to);
 		msg.msg_name=&to->s;
 		msg.msg_namelen=tolen;
 #endif /* __OS_* */
 	}else{
 		tolen=sockaddru_len(*to);
 		msg.msg_name=&to->s;
 		msg.msg_namelen=tolen;
 	}
 	
 again:
 	n=sendmsg(socket, &msg, MSG_DONTWAIT);
 	if (n==-1){
 #ifdef __OS_linux
 		if ((errno==EPIPE) && try_assoc_id){
 			/* try again, this time with null assoc_id and non-null msg.name */
 			DBG("sctp raw sendmsg: assoc already closed (EPIPE), retrying with"
 					" assoc_id=0\n");
 			tolen=sockaddru_len(*to);
 			msg.msg_name=&to->s;
 			msg.msg_namelen=tolen;
 			sinfo->sinfo_assoc_id=0;
 			try_assoc_id=0;
 			goto again;
 		}
 #elif defined __OS_freebsd
 		if ((errno==ENOENT)){
 			/* it didn't work, no retrying */
 			WARN("unexpected sendmsg() failure (ENOENT),"
 					" assoc_id %d\n", sinfo->sinfo_assoc_id);
 		}
 #else /* __OS_* */
 		if ((errno==ENOENT || errno==EPIPE) && try_assoc_id){
 			/* in case the sctp stack prioritises assoc_id over msg->name,
 			   try again with 0 assoc_id and msg->name set to "to" */
 			WARN("unexpected ENOENT or EPIPE (assoc_id %d),"
 					"trying automatic recovery... (please report along with"
 					"your OS version)\n", sinfo->sinfo_assoc_id);
 			tolen=sockaddru_len(*to);
 			msg.msg_name=&to->s;
 			msg.msg_namelen=tolen;
 			sinfo->sinfo_assoc_id=0;
 			try_assoc_id=0;
 			goto again;
 		}
 #endif /* __OS_* */
 #if 0
 		if (errno==EINTR) goto again;
 		su2ip_addr(&ip, to);
 		LOG(L_ERR, "ERROR: sctp_raw_send: sendmsg(sock,%p,%d,0,%s:%d,...):"
 				" %s(%d)\n", buf, len, ip_addr2a(&ip), su_getport(to),
 				strerror(errno), errno);
 		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){
 			SCTP_STATS_SENDQ_FULL();
 			LOG(L_ERR, "ERROR: sctp_msg_send: failed to send, send buffers"
 						" full\n");
 		}
 #endif
 	}
 	return n;
 }
 
 
 
31fd952b
 /* send buf:len over sctp to dst using sndrcv_info (uses send_sock,
  * to and id from dest_info)
c3611173
  * returns the numbers of bytes sent on success (>=0) and -1 on error
  */
92b6de79
 static int sctp_msg_send_ext(struct dest_info* dst, char* buf, unsigned len,
87d68ef9
 						struct sctp_sndrcvinfo* sndrcv_info)
c3611173
 {
 	int n;
 	int tolen;
 	struct ip_addr ip; /* used only on error, for debugging */
 	struct msghdr msg;
 	struct iovec iov[1];
31fd952b
 	struct socket_info* si;
87d68ef9
 	struct sctp_sndrcvinfo* sinfo;
 	struct cmsghdr* cmsg;
 	/* make sure msg_control will point to properly aligned data */
 	union {
 		struct cmsghdr cm;
 		char cbuf[CMSG_SPACE(sizeof(*sinfo))];
 	}ctrl_un;
31fd952b
 #ifdef SCTP_CONN_REUSE
 	int assoc_id;
 	union sockaddr_union to;
 #ifdef SCTP_ADDR_HASH
 	int tmp_id, tmp_assoc_id;
 #endif /* SCTP_ADDR_HASH */
 #endif /* SCTP_CONN_REUSE */
c3611173
 	
 	iov[0].iov_base=buf;
 	iov[0].iov_len=len;
 	msg.msg_iov=iov;
 	msg.msg_iovlen=1;
87d68ef9
 	msg.msg_flags=0; /* not used on send (use instead sinfo_flags) */
 	msg.msg_control=ctrl_un.cbuf;
 	msg.msg_controllen=sizeof(ctrl_un.cbuf);
 	cmsg=CMSG_FIRSTHDR(&msg);
 	cmsg->cmsg_level=IPPROTO_SCTP;
 	cmsg->cmsg_type=SCTP_SNDRCV;
 	cmsg->cmsg_len=CMSG_LEN(sizeof(*sinfo));
 	sinfo=(struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
 	*sinfo=*sndrcv_info;
 	/* some systems need msg_controllen set to the actual size and not
 	 * something bigger (e.g. openbsd) */
 	msg.msg_controllen=cmsg->cmsg_len;
31fd952b
 	si=dst->send_sock;
 #ifdef SCTP_CONN_REUSE
 	/* if dst->id is set it means we want to send on association with
 	   ser id dst->id if still opened and only if closed use dst->to */
 	assoc_id=0;
709fb806
 	if ((dst->id) && cfg_get(sctp, sctp_cfg, assoc_reuse) &&
 			cfg_get(sctp, sctp_cfg, assoc_tracking) &&
61d0ee55
 			(assoc_id=sctp_con_get_assoc(dst->id, &si, &to, 0))){
31fd952b
 		DBG("sctp: sending on sctp assoc_id %d (ser id %d)\n",
 				assoc_id, dst->id);
 		sinfo->sinfo_assoc_id=assoc_id;
 		/* on linux msg->name has priority over assoc_id. To try first assoc_id
 		 * and then dst, one has to call first sendmsg() with msg->name==0 and
 		 * sinfo->assoc_id set. If it returns EPIPE => association is no longer
 		 * open => call again sendmsg() this time with msg->name!=0.
 		 * on freebsd assoc_id has priority over msg->name and moreover the
 		 * send falls back automatically to the address if the assoc_id is
 		 * closed, so a single call to sendmsg(msg->name, sinfo->assoc_id ) is
 		 * enough.  If one tries calling with msg->name==0 and the association
 		 * is no longer open send will return ENOENT.
 		 * on solaris it seems one must always use a dst address (assoc_id
 		 * will be ignored).
 		 */
 #ifdef __OS_linux
 		DBG("sctp: linux: trying with 0 msg_name\n");
 		msg.msg_name=0;
 		msg.msg_namelen=0;
 #elif defined __OS_freebsd
 		tolen=sockaddru_len(dst->to);
 		msg.msg_name=&dst->to.s;
 		msg.msg_namelen=tolen;
 #else /* __OS_* */
 		/* fallback for solaris and others, sent back to
 		  the address recorded (not exactly what we want, but there's 
 		  no way to fallback to dst->to) */
92b6de79
 		tolen=sockaddru_len(dst->to);
 		msg.msg_name=&dst->to.s;
31fd952b
 		msg.msg_namelen=tolen;
 #endif /* __OS_* */
 	}else{
 #ifdef SCTP_ADDR_HASH
 		/* update timeout for the assoc identified  by (dst->to, dst->si) */
709fb806
 		if (likely(cfg_get(sctp, sctp_cfg, assoc_tracking))){
 			tmp_id=sctp_con_addr_get_id_assoc(&dst->to, dst->send_sock,
 												&tmp_assoc_id, 0);
 			DBG("sctp send: timeout updated ser id %d, sctp assoc_id %d\n",
 					tmp_id, tmp_assoc_id);
92b6de79
 			if (tmp_id==0 /* not tracked/found */ &&
 					(unsigned)atomic_get(sctp_conn_tracked) >=
 						(unsigned)cfg_get(sctp, sctp_cfg, max_assocs)){
 				ERR("maximum number of sctp associations exceeded\n");
 				goto error;
 			}
709fb806
 		}
31fd952b
 #endif /* SCTP_ADDR_HASH */
 		tolen=sockaddru_len(dst->to);
 		msg.msg_name=&dst->to.s;
 		msg.msg_namelen=tolen;
 	}
 #else /* SCTP_CONN_REUSE */
 	tolen=sockaddru_len(dst->to);
 	msg.msg_name=&dst->to.s;
 	msg.msg_namelen=tolen;
 #endif /* SCTP_CONN_REUSE */
 
c3611173
 again:
31fd952b
 	n=sendmsg(si->socket, &msg, MSG_DONTWAIT);
c3611173
 	if (n==-1){
31fd952b
 #ifdef SCTP_CONN_REUSE
 #ifdef __OS_linux
 		if ((errno==EPIPE) && assoc_id){
 			/* try again, this time with null assoc_id and non-null msg.name */
 			DBG("sctp sendmsg: assoc already closed (EPIPE), retrying with"
 					" assoc_id=0\n");
 			tolen=sockaddru_len(dst->to);
 			msg.msg_name=&dst->to.s;
 			msg.msg_namelen=tolen;
 			sinfo->sinfo_assoc_id=0;
 			goto again;
 		}
 #elif defined __OS_freebsd
 		if ((errno==ENOENT)){
 			/* it didn't work, no retrying */
 			WARN("sctp sendmsg: unexpected sendmsg() failure (ENOENT),"
 					" assoc_id %d\n", assoc_id);
 		}
 #else /* __OS_* */
 		if ((errno==ENOENT || errno==EPIPE) && assoc_id){
 			/* in case the sctp stack prioritises assoc_id over msg->name,
 			   try again with 0 assoc_id and msg->name set to dst->to */
 			WARN("sctp sendmsg: unexpected ENOENT or EPIPE (assoc_id %d),"
 					"trying automatic recovery... (please report along with"
 					"your OS version)\n", assoc_id);
 			tolen=sockaddru_len(dst->to);
 			msg.msg_name=&dst->to.s;
 			msg.msg_namelen=tolen;
 			sinfo->sinfo_assoc_id=0;
 			goto again;
 		}
 #endif /* __OS_* */
 #endif /* SCTP_CONN_REUSE */
c3611173
 		su2ip_addr(&ip, &dst->to);
31fd952b
 		LOG(L_ERR, "ERROR: sctp_msg_send: sendmsg(sock,%p,%d,0,%s:%d,...):"
c3611173
 				" %s(%d)\n", buf, len, ip_addr2a(&ip), su_getport(&dst->to),
31fd952b
 				strerror(errno), errno);
c3611173
 		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){
7a66235b
 			SCTP_STATS_SENDQ_FULL();
c3611173
 			LOG(L_ERR, "ERROR: sctp_msg_send: failed to send, send buffers"
 						" full\n");
 		}
 	}
 	return n;
92b6de79
 #ifdef SCTP_CONN_REUSE
 #ifdef SCTP_ADDR_HASH
 error:
 	return -1;
 #endif /* SCTP_ADDR_HASH */
 #endif /* SCTP_CONN_REUSE */
c3611173
 }
 
 
 
92b6de79
 /* wrapper around sctp_msg_send_ext():
31fd952b
  * send buf:len over udp to dst (uses only the to, send_sock and id members
87d68ef9
  * from dst)
  * 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)
 {
 	struct sctp_sndrcvinfo sinfo;
57f57a43
 #ifdef HAVE_SCTP_SNDRCVINFO_PR_POLICY
 	int send_ttl;
 #endif
87d68ef9
 	
 	memset(&sinfo, 0, sizeof(sinfo));
 	sinfo.sinfo_flags=SCTP_UNORDERED;
 #ifdef HAVE_SCTP_SNDRCVINFO_PR_POLICY
57f57a43
 	if ((send_ttl=cfg_get(sctp, sctp_cfg, send_ttl))){
87d68ef9
 		sinfo.sinfo_pr_policy=SCTP_PR_SCTP_TTL;
57f57a43
 		sinfo.sinfo_pr_value=send_ttl;
87d68ef9
 	}else
 		sinfo->sinfo_pr_policy=SCTP_PR_SCTP_NONE;
 #else
57f57a43
 		sinfo.sinfo_timetolive=cfg_get(sctp, sctp_cfg, send_ttl);
87d68ef9
 #endif
57f57a43
 	sinfo.sinfo_context=cfg_get(sctp, sctp_cfg, send_retries);
92b6de79
 	return sctp_msg_send_ext(dst, buf, len, &sinfo);
87d68ef9
 }
 
 
 
31fd952b
 /** generic sctp info (basic stats).*/
 void sctp_get_info(struct sctp_gen_info* i)
c3611173
 {
31fd952b
 	if (i){
 		i->sctp_connections_no=atomic_get(sctp_conn_no);
 #ifdef SCTP_CONN_REUSE
709fb806
 		if (likely(cfg_get(sctp, sctp_cfg, assoc_tracking)))
 			i->sctp_tracked_no=atomic_get(sctp_conn_tracked);
 		else
 			i->sctp_tracked_no=-1;
31fd952b
 #else /* SCTP_CONN_REUSE */
 		i->sctp_tracked_no=-1;
 #endif /* SCTP_CONN_REUSE */
 		i->sctp_total_connections=atomic_get(sctp_id);
 	}
c3611173
 }
 
31fd952b
 
c3611173
 #endif /* USE_SCTP */