Squashed 'third_party/lksctp-tools/' content from commit 200eca7f1

Change-Id: I8f7575513f114b205178cac5c6b3706f3d725cb5
git-subtree-dir: third_party/lksctp-tools
git-subtree-split: 200eca7f1419b1ae53958b51e8551f7e7f6cd467
diff --git a/src/apps/sctp_test.c b/src/apps/sctp_test.c
new file mode 100644
index 0000000..cd7654b
--- /dev/null
+++ b/src/apps/sctp_test.c
@@ -0,0 +1,1852 @@
+/* SCTP kernel Implementation
+ * (C) Copyright IBM Corp. 2001, 2003
+ * Copyright (c) 1999 Cisco
+ * Copyright (c) 1999, 2000, 2001 Motorola
+ * Copyright (c) 2001-2002 Nokia
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This is a userspace test application for the SCTP kernel 
+ * implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ^^^^^^^^^^^^^^^^^^^^^^^^
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *   Hui Huang         <hui.huang@nokia.com>
+ *   Sridhar Samudrala <samudrala@us.ibm.com>
+ *   Jon Grimm         <jgrimm@us.ibm.com>
+ *   Daisy Chang       <daisyc@us.ibm.com>
+ *   Ryan Layer	       <rmlayer@us.ibm.com>
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <netinet/sctp.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <net/if.h>
+
+#include <sys/resource.h>
+
+
+#define REALLY_BIG 65536
+
+#define SERVER 		0
+#define CLIENT 		1
+#define MIXED 		2
+#define NOT_DEFINED	666
+
+#define REPEAT 		10
+#define BIG_REPEAT	1000000
+#define MAX_BIND_RETRYS 10
+#define BODYSIZE	10
+#define MSG_CNT		10	/* If this is changed the msg_sizes array
+				   needs to be modified accordingly.  */
+
+#define DEFAULT_MAX_WINDOW 32768
+#define DEFAULT_MIN_WINDOW 1500
+
+#define DEBUG_NONE	0
+#define DEBUG_MIN	1
+#define DEBUG_MAX	2
+
+#define STREAM_PATTERN_SEQUENTIAL 0
+#define STREAM_PATTERN_RANDOM     1
+
+#define ORDER_PATTERN_UNORDERED   0
+#define ORDER_PATTERN_ORDERED     1
+#define ORDER_PATTERN_ALTERNATE   2
+#define ORDER_PATTERN_RANDOM      3
+
+#define ASSOC_PATTERN_SEQUENTIAL 0
+#define ASSOC_PATTERN_RANDOM     1
+
+#define NCASES 6
+#define MAX_POLL_SKS 256
+
+#define DEBUG_PRINT(level, print_this...)	\
+{						\
+	if (debug_level >= level) { 		\
+		fprintf(stdout, print_this); 	\
+		fflush(stdout); 		\
+	}					\
+} /* DEBUG_PRINT */
+
+/* Convenience structure to determine space needed for cmsg. */
+typedef union {
+        struct sctp_initmsg init;
+        struct sctp_sndrcvinfo sndrcvinfo;
+} _sctp_cmsg_data_t;
+
+#ifdef __FreeBSD__
+typedef union {
+        int                    raw;
+        struct sctp_initmsg     init;
+        struct sctp_sndrcvinfo  sndrcv;
+} sctp_cmsg_data_t;
+#endif
+
+#define CMSG_SPACE_INITMSG (CMSG_SPACE(sizeof(struct sctp_initmsg)))
+#define CMSG_SPACE_SNDRCV (CMSG_SPACE(sizeof(struct sctp_sndrcvinfo)))
+
+typedef struct {
+	int rem_port;
+        int order_state;
+        int stream_state;
+	int msg_cnt;
+	int msg_sent;
+	int cycle;
+} _assoc_state;
+
+typedef struct {
+        int sk;
+        int assoc_i;
+	_assoc_state *assoc_state;
+} _poll_sks;
+
+char *local_host = NULL;
+int local_port = 0;
+char *remote_host = NULL;
+int remote_port = 0;
+/* struct sockaddr_in s_rem, s_loc; */
+struct sockaddr_storage s_rem, s_loc;
+int r_len, l_len;
+int test_case = 0;
+int size_arg = 0;
+int xflag = 0;
+int debug_level = DEBUG_MAX;
+int do_exit = 1;
+int stream_pattern = STREAM_PATTERN_SEQUENTIAL;
+int stream_state = 0;
+int order_pattern = ORDER_PATTERN_UNORDERED;
+int order_state = 0;
+int max_stream = 0;
+int seed = 0;
+int max_msgsize = DEFAULT_MAX_WINDOW;
+int timetolive = 0;
+int assoc_pattern = ASSOC_PATTERN_SEQUENTIAL;
+int socket_type = SOCK_SEQPACKET;
+int repeat_count = 0;
+int listeners = 0;
+int tosend = 0;
+_poll_sks poll_sks[MAX_POLL_SKS];
+int repeat = REPEAT;
+int msg_cnt = MSG_CNT;
+int drain = 0;
+int role = NOT_DEFINED;
+struct sockaddr *bindx_add_addrs = NULL;
+int bindx_add_count = 0;
+struct sockaddr *connectx_addrs = NULL;
+int connectx_count = 0;
+int if_index = 0;
+
+unsigned char msg[] = "012345678901234567890123456789012345678901234567890";
+
+static int msg_sizes[NCASES][MSG_CNT] =
+	{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+	 {1452, 2904, 4356, 1452, 2904, 4356, 1452, 2904, 4356, 1452},
+	 {1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453},
+	 {1, 1453, 32768, 1, 1453, 32768, 1, 1453, 32768, 1},
+	 {1, 1000, 2000, 3000, 5000, 10000, 15000, 20000, 25000, 32768},
+	 {32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768},
+	};
+
+static const char *sac_state_tbl[] = {
+	"COMMUNICATION_UP",
+	"COMMUNICATION_LOST",
+	"RESTART",
+	"SHUTDOWN_COMPLETE",
+	"CANT_START_ASSOCICATION"
+};
+
+void usage(char *argv0)
+{
+	fprintf(stderr, "\nusage:\n");
+	fprintf(stderr, "  server:\n");
+	fprintf(stderr, "  %8s -H local-addr -P local-port -l [-d level] [-x]\n"
+			"\t      [-L num-ports] [-S num-ports]\n"
+			"\t      [-a assoc-pattern]\n"
+			"\t      [-i interface]\n",
+		argv0);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "  client:\n");
+	fprintf(stderr, "  %8s -H local-addr -P local-port -h remote-addr\n"
+		"\t      -p remote-port -s [-c case ] [-d level]\n"
+		"\t      [-x repeat] [-o order-pattern] [-t stream-pattern]\n"
+		"\t      [-M max-stream] [-r rand-seed]\n"
+		"\t      [-m max-msgsize]\n"
+		"\t      [-L num-ports] [-S num-ports]\n"
+		"\t      [-a assoc-pattern]\n"
+		"\t      [-i interface]\n",
+		argv0);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "\t-a assoc_pattern in the mixed mode\n");
+	fprintf(stderr, "\t   0 = sequential ascending(default)\n");
+	fprintf(stderr, "\t   1 = random\n");
+	fprintf(stderr, "\t-d debug\n");
+	fprintf(stderr, "\t   0 = none\n");
+	fprintf(stderr, "\t   1 = min(default)\n");
+	fprintf(stderr, "\t   2 = max\n");
+	fprintf(stderr, "\t-c testcase\n");
+	fprintf(stderr, "\t   0 = 1 byte packets.\n");
+	fprintf(stderr, "\t   1 = Sequence of multiples of 1452 byte packets.\n");
+	fprintf(stderr, "\t       (1452 is fragmentation point for an i/f with ");
+	fprintf(stderr, "1500 as mtu.)\n");
+	fprintf(stderr, "\t   2 = 1453 byte packets.\n");
+	fprintf(stderr, "\t       (min. size at which fragmentation occurs\n");
+	fprintf(stderr, "\t        for an i/f with 1500 as mtu.)\n");
+	fprintf(stderr, "\t   3 = Sequence of 1, 1453, 32768 byte packets.\n");
+	fprintf(stderr, "\t   4 = Sequence of following size packets.\n");
+	fprintf(stderr, "\t       (1, 1000, 2000, 3000, 5000, 10000,");
+	fprintf(stderr, "15000, 20000, 25000, 32768)\n");
+	fprintf(stderr, "\t   5 = 32768 byte packets.\n");
+	fprintf(stderr, "\t       (default max receive window size.)\n");
+	fprintf(stderr, "\t   6 = random size packets.\n");
+	fprintf(stderr, "\t   -ve value = Packets of specifed size.\n");
+	fprintf(stderr, "\t-m max msgsize for option -c 6 (1500-65515, default value 32768)\n");
+	fprintf(stderr, "\t-x number of repeats\n");
+	fprintf(stderr, "\t-o order-pattern\n");
+	fprintf(stderr, "\t   0 = all unordered(default) \n");
+	fprintf(stderr, "\t   1 = all ordered \n");
+	fprintf(stderr, "\t   2 = alternating \n");
+        fprintf(stderr, "\t   3 = random\n");
+	fprintf(stderr, "\t-t stream-pattern\n");
+	fprintf(stderr, "\t   0 = sequential ascending(default)\n");
+	fprintf(stderr, "\t   1 = random\n");
+	fprintf(stderr, "\t-M max-stream (default value 0)\n");
+	fprintf(stderr, "\t-r seed (default 0, use time())\n");
+	fprintf(stderr, "\t-L num-ports (default value 0). Run the mixed mode\n");
+	fprintf(stderr, "\t-S num-ports (default value 0). Run the mixed mode\n");
+	fprintf(stderr, "\t-D drain. If in client mode do a read following send.\n");
+	fprintf(stderr, "\t-T use SOCK_STREAM tcp-style sockets.\n");
+	fprintf(stderr, "\t-B add the specified address(es) as additional bind\n");
+	fprintf(stderr, "\t   addresses of the local socket. Multiple addresses can\n");
+	fprintf(stderr, "\t   be specified by using this argument multiple times.\n");
+	fprintf(stderr, "\t   For example, '-B 10.0.0.1 -B 20.0.0.2'.\n");
+	fprintf(stderr, "\t   In case of IPv6 linklocal address, interface name can be set in following way \n");
+	fprintf(stderr, "\t   For example, '-B fe80::f8c3:b77f:698e:4506%%eth2'.\n");
+	fprintf(stderr, "\t-C use the specified address(es) for connection to the\n");
+	fprintf(stderr, "\t   peer socket. Multiple addresses can be specified by\n");
+	fprintf(stderr, "\t   using this argument multiple times.\n");
+	fprintf(stderr, "\t   For example, '-C 10.0.0.1 -C 20.0.0.2'.\n");
+	fprintf(stderr, "\t   This option is incompatible with the -h option.\n");
+	fprintf(stderr, "\t-O time to live (default value 0)\n");
+	fprintf(stderr, "\n");
+	fflush(stderr);
+
+} /* usage() */
+
+void *
+build_msg(int len)
+{
+	int i = len - 1;
+	int n;
+	char *msg_buf, *p;
+
+	msg_buf = malloc(len);
+	if (NULL == msg_buf) {
+		fprintf(stderr, "\n\t\t*** malloc not enough memory!!! ***\n");
+		exit(1);
+	}
+	p = msg_buf;
+
+	do {
+		n = ((i > 50)?50:i);
+		memcpy(p, msg, ((i > 50)?50:i));
+		p += n;
+		i -= n;
+	} while (i > 0);
+
+	msg_buf[len-1] = '\0';
+
+	return(msg_buf);
+
+} /* build_msg() */
+
+static int
+print_cmsg(int type, sctp_cmsg_data_t *data)
+{
+        switch(type) {
+        case SCTP_INIT:
+		DEBUG_PRINT(DEBUG_MAX, "\tINIT\n");
+		if (DEBUG_MAX == debug_level) {
+			printf("\t\tsinit_num_ostreams=%d ",
+			       data->init.sinit_num_ostreams);
+                	printf("sinit_max_instreams=%d ",
+			       data->init.sinit_max_instreams);
+	                printf("sinit_max_attempts=%d ",
+			       data->init.sinit_max_attempts);
+        	        printf("sinit_max_init_timeo=%d\n",
+			       data->init.sinit_max_init_timeo);
+		}
+		break;
+        case SCTP_SNDRCV:
+		DEBUG_PRINT(DEBUG_MAX, "\t  SNDRCV");
+		if (DEBUG_MAX == debug_level) {
+        	        printf("(stream=%u ", 	data->sndrcv.sinfo_stream);
+                	printf("ssn=%u ", 	data->sndrcv.sinfo_ssn);
+			printf("tsn=%u ", 	data->sndrcv.sinfo_tsn);
+	                printf("flags=0x%x ",	data->sndrcv.sinfo_flags);
+        	        printf("ppid=%u\n",	data->sndrcv.sinfo_ppid);
+			printf("cumtsn=%u\n",   data->sndrcv.sinfo_cumtsn);
+                }
+		break;
+         default:
+		DEBUG_PRINT(DEBUG_MIN, "\tUnknown type: %d\n", type);
+                break;
+        }
+	fflush(stdout);
+        return 0;
+
+} /* print_cmsg() */
+
+/* This function prints the message. */
+static int
+print_message(const int sk, struct msghdr *msg, size_t msg_len) {
+	struct cmsghdr *scmsg;
+	sctp_cmsg_data_t *data;
+        int i;
+
+        if (!(MSG_NOTIFICATION & msg->msg_flags)) {
+                int index = 0;
+
+		DEBUG_PRINT(DEBUG_MIN, "Data %zu bytes.", msg_len);
+		DEBUG_PRINT(DEBUG_MAX, " First %zu bytes: ",
+				    (msg_len < BODYSIZE)?msg_len:BODYSIZE);
+                /* Make sure that everything is printable and that we
+                 * are NUL terminated...
+                 */
+		while ( msg_len > 0 ) {
+			char *text, tmptext[BODYSIZE];
+			int len;
+
+			memset(tmptext, 0x0, BODYSIZE);
+
+			text = msg->msg_iov[index].iov_base;
+			len = msg->msg_iov[index].iov_len;
+
+			if (msg_len == 1 && text[0] == 0) {
+				DEBUG_PRINT(DEBUG_MIN, "<empty> text[0]=%d",
+					    text[0]);
+				break;
+			}
+
+			if ( len > msg_len ) {
+				/* text[(len = msg_len) - 1] = '\0'; */
+				text[(len = msg_len)] = '\0';
+			}
+
+			if ( (msg_len -= len) > 0 ) { index++; }
+
+			for (i = 0; i < len - 1; ++i) {
+				if (!isprint(text[i])) text[i] = '.';
+			}
+
+ 			strncpy(tmptext, text, BODYSIZE);
+ 			tmptext[BODYSIZE-1] = '\0';
+
+			DEBUG_PRINT(DEBUG_MAX, "%s", tmptext);
+                }
+
+                DEBUG_PRINT(DEBUG_MIN, "\n");
+                fflush(stdout);
+        }  else { /* if(we have notification) */
+		struct sctp_assoc_change *sac;
+		struct sctp_send_failed *ssf;
+		struct sctp_paddr_change *spc;
+		struct sctp_remote_error *sre;
+		union sctp_notification *snp;
+		char addrbuf[INET6_ADDRSTRLEN];
+		const char *ap;
+		struct sockaddr_in *sin;
+		struct sockaddr_in6 *sin6;
+		int index = 0;
+
+		snp = (union sctp_notification *)msg->msg_iov[index].iov_base;
+
+		DEBUG_PRINT(DEBUG_MIN, "Notification:");
+        	
+		switch (snp->sn_header.sn_type) {
+			case SCTP_ASSOC_CHANGE:
+				sac = &snp->sn_assoc_change;
+				DEBUG_PRINT(DEBUG_MIN,
+					    " SCTP_ASSOC_CHANGE(%s)\n",
+					    sac_state_tbl[sac->sac_state]);
+				DEBUG_PRINT(DEBUG_MAX,
+					    "\t\t(assoc_change: state=%hu, "
+					    "error=%hu, instr=%hu "
+					    "outstr=%hu)\n",
+					    sac->sac_state, sac->sac_error,
+					    sac->sac_inbound_streams,
+					    sac->sac_outbound_streams);
+				break;
+			case SCTP_PEER_ADDR_CHANGE:
+				spc = &snp->sn_paddr_change;
+				DEBUG_PRINT(DEBUG_MIN,
+					    " SCTP_PEER_ADDR_CHANGE\n");
+				if (spc->spc_aaddr.ss_family == AF_INET) {
+					sin = (struct sockaddr_in *)
+					       &spc->spc_aaddr;
+					ap = inet_ntop(AF_INET, &sin->sin_addr,
+						       addrbuf,
+						       INET6_ADDRSTRLEN);
+				} else {
+					sin6 = (struct sockaddr_in6 *)
+						&spc->spc_aaddr;
+					ap = inet_ntop(AF_INET6,
+						       &sin6->sin6_addr,
+						       addrbuf,
+						       INET6_ADDRSTRLEN);
+				}
+				DEBUG_PRINT(DEBUG_MAX,
+					    "\t\t(peer_addr_change: %s "
+					    "state=%d, error=%d)\n",
+					    ap, spc->spc_state,
+					    spc->spc_error);
+				break;
+			case SCTP_SEND_FAILED:
+				ssf = &snp->sn_send_failed;
+				DEBUG_PRINT(DEBUG_MIN,
+					    " SCTP_SEND_FAILED\n");
+				DEBUG_PRINT(DEBUG_MAX,
+					    "\t\t(sendfailed: len=%hu "
+					    "err=%d)\n",
+					    ssf->ssf_length, ssf->ssf_error);
+				break;
+			case SCTP_REMOTE_ERROR:
+				sre = &snp->sn_remote_error;
+				DEBUG_PRINT(DEBUG_MIN,
+					    " SCTP_REMOTE_ERROR\n");
+				DEBUG_PRINT(DEBUG_MAX,
+					    "\t\t(remote_error: err=%hu)\n",
+					     ntohs(sre->sre_error));
+				break;
+			case SCTP_SHUTDOWN_EVENT:
+				DEBUG_PRINT(DEBUG_MIN,
+					    " SCTP_SHUTDOWN_EVENT\n");
+				break;
+			default:
+				DEBUG_PRINT(DEBUG_MIN,
+					    " Unknown type: %hu\n",
+					    snp->sn_header.sn_type);
+				break;
+		}
+
+		fflush(stdout);
+		return 1;
+
+        } /* notification received */
+
+        for (scmsg = CMSG_FIRSTHDR(msg);
+             scmsg != NULL;
+             scmsg = CMSG_NXTHDR(msg, scmsg)) {
+
+		data = (sctp_cmsg_data_t *)CMSG_DATA(scmsg);
+		if (debug_level) print_cmsg(scmsg->cmsg_type, data);
+	}
+
+
+	fflush(stdout);
+        return 0;
+
+} /* print_message() */
+
+struct sockaddr *
+append_addr(const char *parm, struct sockaddr *addrs, int *ret_count)
+{
+	struct sockaddr *new_addrs = NULL;
+	void *aptr;
+	struct sockaddr *sa_addr;
+	struct sockaddr_in *b4ap;
+	struct sockaddr_in6 *b6ap;
+	struct hostent *hst4 = NULL;
+	struct hostent *hst6 = NULL;
+	int i4 = 0;
+	int i6 = 0;
+	int j;
+	int orig_count = *ret_count;
+	int count = orig_count;
+	char *ipaddr = strdup(parm);
+	char *ifname;
+	int ifindex = 0;
+
+	/* check the interface. */
+	ifname = strchr(ipaddr,'%');
+	if (ifname) {
+		*ifname=0;
+		ifname++;
+		ifindex = if_nametoindex(ifname);
+		if (!ifindex) {
+			fprintf(stderr, "bad interface name: %s\n", ifname);
+			goto finally;
+		}
+	}
+
+	/* Get the entries for this host.  */
+	hst4 = gethostbyname(ipaddr);
+	hst6 = gethostbyname2(ipaddr, AF_INET6);
+
+	if ((NULL == hst4 || hst4->h_length < 1)
+	    && (NULL == hst6 || hst6->h_length < 1)) {
+		fprintf(stderr, "bad hostname: %s\n", ipaddr);
+		goto finally;
+	}
+
+	/* Figure out the number of addresses.  */
+	if (NULL != hst4) {
+		for (i4 = 0; NULL != hst4->h_addr_list[i4]; ++i4) {
+			count++;
+		}
+	}
+	if (NULL != hst6) {
+		for (i6 = 0; NULL != hst6->h_addr_list[i6]; ++i6) {
+			count++;
+		}
+	}
+
+	/* Expand memory for the new addresses.  Assume all the addresses
+	 * are v6 addresses.
+	 */
+	new_addrs = (struct sockaddr *)
+		realloc(addrs, sizeof(struct sockaddr_in6) * count);
+
+	if (NULL == new_addrs) {
+		count = *ret_count;
+		goto finally;
+	}
+
+	/* Skip the existing addresses. */
+	aptr = new_addrs;
+	for (j = 0; j < orig_count; j++) {
+		sa_addr = (struct sockaddr *)aptr;
+		switch(sa_addr->sa_family) {
+		case AF_INET:
+			aptr += sizeof(struct sockaddr_in);
+			break;
+		case AF_INET6:
+			aptr += sizeof(struct sockaddr_in6);
+			break;
+		default:
+			count = orig_count;
+			goto finally;
+		}
+	}
+
+	/* Put the new addresses away.  */
+	if (NULL != hst4) {
+		for (j = 0; j < i4; ++j) {
+			b4ap = (struct sockaddr_in *)aptr;
+			memset(b4ap, 0x00, sizeof(*b4ap));
+			b4ap->sin_family = AF_INET;
+			b4ap->sin_port = htons(local_port);
+			bcopy(hst4->h_addr_list[j], &b4ap->sin_addr,
+			      hst4->h_length);
+
+			aptr += sizeof(struct sockaddr_in);
+		} /* for (loop through the new v4 addresses) */
+	}
+
+	if (NULL != hst6) {
+		for (j = 0; j < i6; ++j) {
+			b6ap = (struct sockaddr_in6 *)aptr;
+			memset(b6ap, 0x00, sizeof(*b6ap));
+			b6ap->sin6_family = AF_INET6;
+			b6ap->sin6_port =  htons(local_port);
+			b6ap->sin6_scope_id = if_index;
+			bcopy(hst6->h_addr_list[j], &b6ap->sin6_addr,
+			      hst6->h_length);
+			if (!ifindex) {
+				b6ap->sin6_scope_id = ifindex;
+			}
+
+			aptr += sizeof(struct sockaddr_in6);
+		} /* for (loop through the new v6 addresses) */
+	}
+
+ finally:
+	free(ipaddr);
+	*ret_count = count;
+
+	return new_addrs;
+
+} /* append_addr() */
+
+int socket_r(void)
+{
+	struct sctp_event_subscribe subscribe;
+	int sk, error;
+
+	DEBUG_PRINT(DEBUG_MIN, "\tsocket(%s, IPPROTO_SCTP)",
+		(socket_type == SOCK_SEQPACKET) ? "SOCK_SEQPACKET" : "SOCK_STREAM");
+
+	if ((sk = socket(s_loc.ss_family, socket_type, IPPROTO_SCTP)) < 0 ) {
+		if (do_exit) {
+			fprintf(stderr, "\n\n\t\t*** socket: failed to create"
+				" socket:  %s ***\n",
+        	       	        strerror(errno));
+			exit(1);
+		} else {
+			return -1;
+		}
+	}
+	DEBUG_PRINT(DEBUG_MIN, "  ->  sk=%d\n", sk);
+
+	memset(&subscribe, 0, sizeof(subscribe));
+	subscribe.sctp_data_io_event = 1;
+	subscribe.sctp_association_event = 1;
+	error = setsockopt(sk, SOL_SCTP, SCTP_EVENTS, (char *)&subscribe,
+			   sizeof(subscribe));
+	if (error) {
+		fprintf(stderr, "SCTP_EVENTS: error: %d\n", error);
+		exit(1);
+	}
+        if (max_stream > 0) {
+        	struct sctp_initmsg initmsg;
+        	memset(&initmsg, 0, sizeof(struct sctp_initmsg));
+        	initmsg.sinit_num_ostreams = max_stream;
+        	error = setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg));
+        	if (error) {
+        		fprintf(stderr, "SCTP_INITMSG: error: %d\n", error);
+        		exit(1);
+        	}
+        }
+	return sk;
+
+} /* socket_r() */
+
+int bind_r(int sk, struct sockaddr_storage *saddr)
+{
+        int error = 0, i = 0;
+	char *host_s, *serv_s;
+
+	if ((host_s = malloc(NI_MAXHOST)) == NULL) {
+		fprintf(stderr, "\n\t\t*** host_s malloc failed!!! ***\n");
+		exit(1);
+	}
+	if ((serv_s = malloc(NI_MAXSERV)) == NULL) {
+		fprintf(stderr, "\n\t\t*** serv_s malloc failed!!! ***\n");
+		exit(1);
+	}
+
+	do {
+		if (i > 0) sleep(1); /* sleep a while before new try... */
+
+		error = getnameinfo((struct sockaddr *)saddr, l_len, host_s,
+				    NI_MAXHOST, serv_s, NI_MAXSERV,
+				    NI_NUMERICHOST);
+
+		if (error)
+			printf("%s\n", gai_strerror(error));
+
+       		DEBUG_PRINT(DEBUG_MIN,
+			    "\tbind(sk=%d, [a:%s,p:%s])  --  attempt %d/%d\n",
+       			    sk, host_s, serv_s, i+1, MAX_BIND_RETRYS);
+
+       		error = bind(sk, (struct sockaddr *)saddr, l_len);
+
+	        if (error != 0) {
+	        	if( errno != EADDRINUSE ) {
+	        		if (do_exit) {
+	        		        fprintf(stderr, "\n\n\t\t***bind: can "
+						"not bind to %s:%s: %s ****\n",
+						host_s, serv_s,
+						strerror(errno));
+					exit(1);
+				} else {
+					return -1;
+				}
+			}
+		}
+		i++;
+		if (i >= MAX_BIND_RETRYS) {
+			fprintf(stderr, "Maximum bind() attempts. "
+				"Die now...\n\n");
+			exit(1);
+		}
+        } while (error < 0 && i < MAX_BIND_RETRYS);
+
+	free(host_s);
+	free(serv_s);
+	return 0;
+
+} /* bind_r() */
+
+int
+bindx_r(int sk, struct sockaddr *addrs, int count, int flag)
+{
+	int error;
+	int i;
+	struct sockaddr *sa_addr;
+	void *aptr;
+
+	/* Set the port in every address.  */
+	aptr = addrs;
+	for (i = 0; i < count; i++) {
+		sa_addr = (struct sockaddr *)aptr;
+
+		switch(sa_addr->sa_family) {
+		case AF_INET:
+			((struct sockaddr_in *)sa_addr)->sin_port =
+				htons(local_port);
+			aptr += sizeof(struct sockaddr_in);
+			break;
+		case AF_INET6:
+			((struct sockaddr_in6 *)sa_addr)->sin6_port =
+				htons(local_port);
+			aptr += sizeof(struct sockaddr_in6);
+			break;
+		default:
+			fprintf(stderr, "Invalid address family\n");
+			exit(1);
+		}
+	}
+
+	error = sctp_bindx(sk, addrs, count, flag);
+	if (error != 0) {
+		fprintf(stderr, "\n\n\t\t***bindx_r: error adding addrs:"
+			" %s. ***\n", strerror(errno));
+		exit(1);
+	}
+
+	return 0;
+
+} /* bindx_r() */
+
+int listen_r(int sk, int listen_count)
+{
+	int error = 0;
+	
+        DEBUG_PRINT(DEBUG_MIN, "\tlisten(sk=%d,backlog=%d)\n",
+		    sk, listen_count);
+
+ 	/* Mark sk as being able to accept new associations */
+        error = listen(sk, listen_count);
+        if (error != 0) {
+        	if (do_exit) {
+                	fprintf(stderr, "\n\n\t\t*** listen:  %s ***\n\n\n",
+                       		strerror(errno));
+			exit(1);
+		}
+		else return -1;
+        }
+        return 0;
+
+} /* listen_r() */
+
+int accept_r(int sk){
+	socklen_t len = 0;
+	int subsk;
+
+	DEBUG_PRINT(DEBUG_MIN, "\taccept(sk=%d)\n", sk);
+
+	subsk = accept(sk, NULL, &len);
+	if (subsk < 0) {
+		fprintf(stderr, "\n\n\t\t*** accept:  %s ***\n\n\n", strerror(errno));
+		exit(1);
+	}
+
+	return subsk;
+} /* accept_r() */
+
+int connect_r(int sk, const struct sockaddr *serv_addr, socklen_t addrlen)
+{
+	int error = 0;
+
+	DEBUG_PRINT(DEBUG_MIN, "\tconnect(sk=%d)\n", sk);
+
+	/* Mark sk as being able to accept new associations */
+	error = connect(sk, serv_addr, addrlen);
+	if (error != 0) {
+		if (do_exit) {
+			fprintf(stderr, "\n\n\t\t*** connect:  %s ***\n\n\n",
+				strerror(errno));
+			exit(1);
+		}
+		else return -1;
+	}
+	return 0;
+
+} /* connect_r() */
+
+int connectx_r(int sk, struct sockaddr *addrs, int count)
+{
+	int error;
+	int i;
+	struct sockaddr *sa_addr;
+	void *aptr;
+
+	/* Set the port in every address.  */
+	aptr = addrs;
+	for (i = 0; i < count; i++) {
+		sa_addr = (struct sockaddr *)aptr;
+
+		switch(sa_addr->sa_family) {
+		case AF_INET:
+			((struct sockaddr_in *)sa_addr)->sin_port =
+				htons(remote_port);
+			aptr += sizeof(struct sockaddr_in);
+			break;
+		case AF_INET6:
+			((struct sockaddr_in6 *)sa_addr)->sin6_port =
+				htons(remote_port);
+			aptr += sizeof(struct sockaddr_in6);
+			break;
+		default:
+			fprintf(stderr, "Invalid address family\n");
+			exit(1);
+		}
+	}
+
+	error = sctp_connectx(sk, addrs, count, NULL);
+	if (error != 0) {
+		fprintf(stderr, "\n\n\t\t*** connectx_r: error connecting"
+			" to addrs: %s ***\n", strerror(errno));
+		exit(1);
+	}
+
+	return 0;
+
+} /* connectx_r() */
+
+int receive_r(int sk, int once)
+{
+	int recvsk = sk, i = 0, error = 0;
+        char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
+        struct iovec iov;
+        struct msghdr inmessage;
+
+        /* Initialize inmessage with enough space for DATA... */
+        memset(&inmessage, 0, sizeof(inmessage));
+        if ((iov.iov_base = malloc(REALLY_BIG)) == NULL) {
+		fprintf(stderr, "\n\t\t*** malloc not enough memory!!! ***\n");
+		exit(1);
+	}
+	iov.iov_len = REALLY_BIG;
+	inmessage.msg_iov = &iov;
+	inmessage.msg_iovlen = 1;
+	/* or a control message.  */
+	inmessage.msg_control = incmsg;
+	inmessage.msg_controllen = sizeof(incmsg);
+
+	/* Get the messages sent */
+	while (1) {
+
+		if (recvsk == sk && socket_type == SOCK_STREAM &&
+		    role == SERVER)
+			recvsk = accept_r(sk);
+
+		DEBUG_PRINT(DEBUG_MIN, "\trecvmsg(sk=%d) ", sk);
+
+		error = recvmsg(recvsk, &inmessage, MSG_WAITALL);
+		if (error < 0 && errno != EAGAIN) {
+			if (errno == ENOTCONN && socket_type == SOCK_STREAM &&
+			    role == SERVER) {
+				printf("No association is present now!!\n");
+				close(recvsk);
+				recvsk = sk;
+				continue;
+			}
+
+			fprintf(stderr, "\n\t\t*** recvmsg: %s ***\n\n",
+					strerror(errno));
+			fflush(stdout);
+			if (do_exit) exit(1);
+			else goto error_out;
+		}
+		else if (error == 0) {
+			if (socket_type == SOCK_STREAM && role == SERVER) {
+				printf("No association is present now!!\n");
+				close(recvsk);
+				recvsk = sk;
+				continue;
+			}
+			printf("\n\t\trecvmsg() returned 0 !!!!\n");
+			fflush(stdout);
+		}
+
+		if (print_message(recvsk, &inmessage, error) > 0)
+			continue; /* got a notification... */
+
+		inmessage.msg_control = incmsg;
+		inmessage.msg_controllen = sizeof(incmsg);
+		iov.iov_len = REALLY_BIG;
+		i++;
+		if (once)
+			break;
+	}
+
+	if (recvsk != sk)
+		close(recvsk);
+
+	free(iov.iov_base);
+	return 0;
+error_out:
+	close(sk);
+	free(iov.iov_base);
+	return -1;
+
+} /* receive_r () */
+
+int next_order(int state, int pattern)
+{
+	switch (pattern){
+	case ORDER_PATTERN_UNORDERED:
+		state = 0;
+		break;
+	case ORDER_PATTERN_ORDERED:
+		state = 1;
+		break;
+	case ORDER_PATTERN_ALTERNATE:
+		state = state ? 0 : 1;
+		break;
+	case ORDER_PATTERN_RANDOM:
+		state = rand() % 2;
+		break;
+	}
+
+	return state;
+}
+
+int next_stream(int state, int pattern)
+{
+	switch (pattern){
+	case STREAM_PATTERN_RANDOM:
+		state = rand() % (max_stream == 0 ? 1 : max_stream);
+		break;
+	case STREAM_PATTERN_SEQUENTIAL:
+		state = state + 1;
+		if (state >= max_stream)
+			state = 0;
+		break;
+	}
+
+	return state;
+}
+
+int next_msg_size(int msg_cnt)
+{
+	int msg_size;
+
+	if (size_arg) {
+		msg_size = size_arg;
+	} else if (test_case < NCASES) {
+		msg_size = msg_sizes[test_case][msg_cnt];
+	} else {
+		msg_size = (rand() % max_msgsize) + 1;
+	}
+
+	return msg_size;
+
+} /* next_msg_size() */
+
+int next_assoc(int i, int state, int pattern)
+{
+	int j;
+	int found = 0;
+	_assoc_state *as;
+
+
+	switch (pattern){
+	case ASSOC_PATTERN_RANDOM:
+		state = rand() % tosend;
+		break;
+	case ASSOC_PATTERN_SEQUENTIAL:
+		state = state + 1;
+		if (state >= tosend)
+			state = 0;
+		break;
+	}
+
+	as = poll_sks[i].assoc_state;
+	j = state;
+	do {
+		if (as[j].msg_sent < repeat_count) {
+			found = 1;
+			break;
+		}
+		if (++j >= tosend) {
+			j = 0;
+		}
+	} while (j != state);
+
+	if (found) {
+		return j;
+	} else {
+		return -1;
+	}
+	
+} /* next_assoc() */
+
+int send_r(int sk, int stream, int order, int send_size, int assoc_i)
+{
+	int error = 0;
+	struct msghdr outmsg;
+	struct iovec iov;
+	char *message = NULL;
+	int msglen = 0;
+	char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+	struct cmsghdr *cmsg;
+	struct sctp_sndrcvinfo *sinfo;
+
+	if (send_size > 0) {
+		message = build_msg(send_size);
+		msglen = strlen(message) + 1;
+		iov.iov_base = message;
+		iov.iov_len = msglen;
+	}
+	else {
+		if (do_exit) {
+			exit(1);
+		} else {
+			goto error_out;
+		}
+	}
+
+	outmsg.msg_name = &s_rem;
+       	outmsg.msg_namelen = sizeof(struct sockaddr_storage);
+	outmsg.msg_iov = &iov;
+	outmsg.msg_iovlen = 1;
+	outmsg.msg_control = outcmsg;
+	outmsg.msg_controllen = sizeof(outcmsg);
+	outmsg.msg_flags = 0;
+
+	cmsg = CMSG_FIRSTHDR(&outmsg);
+	cmsg->cmsg_level = IPPROTO_SCTP;
+	cmsg->cmsg_type = SCTP_SNDRCV;
+	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+
+	outmsg.msg_controllen = cmsg->cmsg_len;
+	sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+	memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
+	sinfo->sinfo_ppid = rand();
+	sinfo->sinfo_stream = stream;
+	sinfo->sinfo_flags = 0;
+	if (!order)
+		sinfo->sinfo_flags = SCTP_UNORDERED;
+	if (timetolive)
+		sinfo->sinfo_timetolive = timetolive;
+
+	DEBUG_PRINT(DEBUG_MIN, "\tsendmsg(sk=%d, assoc=%d) %4d bytes.\n",
+		    sk, assoc_i, send_size);
+	DEBUG_PRINT(DEBUG_MAX, "\t  SNDRCV");
+	if (DEBUG_MAX == debug_level) {
+        	printf("(stream=%u ", 	sinfo->sinfo_stream);
+		printf("flags=0x%x ",	sinfo->sinfo_flags);
+		printf("ppid=%u\n",	sinfo->sinfo_ppid);
+	}
+
+	/* Send to our neighbor.  */
+	error = sendmsg(sk, &outmsg, MSG_WAITALL);
+	if (error != msglen) {
+		fprintf(stderr, "\n\t\t*** sendmsg: %s ***\n\n",
+			strerror(errno));
+		fflush(stdout);
+	
+		if (do_exit) {
+			exit(1);
+		} else {
+			if (!drain)
+				goto error_out;
+		}
+	}
+
+	if (send_size > 0) free(message);
+	return 0;
+error_out:
+	if (send_size > 0) free(message);
+	return -1;
+
+} /* send_r() */
+
+int close_r(int sk)
+{
+	int error = 0;
+	
+	DEBUG_PRINT(DEBUG_MIN, "\tclose(sk=%d)\n",sk);
+
+	error = close(sk);
+	if (error != 0) {
+		if (do_exit) {
+			fprintf(stderr, "\n\n\t\t*** close: %s ***\n\n",
+				strerror(errno));
+			exit(1);
+		} else {
+			return -1;
+		}
+	}
+	fflush(stdout);
+	return 0;
+
+} /* close_r() */
+
+void
+server(int sk)
+{
+	if (max_msgsize > DEFAULT_MAX_WINDOW) {
+		if (setsockopt(sk, SOL_SOCKET, SO_RCVBUF, &max_msgsize,
+			       sizeof(max_msgsize)) < 0) {
+			perror("setsockopt(SO_RCVBUF)");
+			exit(1);
+		} 	
+	}
+
+	receive_r(sk, 0);
+
+} /* server() */
+
+void
+client(int sk)
+{
+	int msg_size;
+	int i;
+
+	for (i = 0; i < msg_cnt; i++) {
+
+		msg_size = next_msg_size(i);
+		order_state = next_order(order_state, order_pattern);
+		stream_state = next_stream(stream_state, stream_pattern);
+
+		if (send_r(sk, stream_state, order_state, msg_size, 0) < 0)
+			break;
+		/* The sender is echoing so do discard the echoed data. */
+		if (drain) {
+			receive_r(sk, 1);
+		}
+	}
+} /* client() */
+
+void
+mixed_mode_test(void)
+{
+	int error, i, j, max_fd, sks, size;
+	int assoc_i, n_msg_size, n_order, n_stream;
+	int done = 0;
+	fd_set *ibitsp = NULL, *obitsp = NULL, *xbitsp = NULL;
+        char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
+        struct iovec iov;
+        struct msghdr inmessage;
+	_assoc_state *as;
+
+	
+	/* Set up the listeners.  If listeners is 0, set up one socket for
+	 * transmitting only.
+	 */
+	iov.iov_base = NULL;
+	max_fd = -1;
+	sks = (0 == listeners) ? 1 : listeners;
+	memset(poll_sks, 0, sks * sizeof(_poll_sks));
+
+	for (i = 0; i < sks; i++) {
+		poll_sks[i].sk = socket_r();
+
+		if (s_loc.ss_family == AF_INET6)
+			( (struct sockaddr_in6 *)&s_loc)->sin6_port =
+				htons(local_port + i);
+		else
+			( (struct sockaddr_in *)&s_loc)->sin_port =
+				htons(local_port + i);
+
+		bind_r(poll_sks[i].sk, &s_loc);
+		if (listeners) {
+			listen_r(poll_sks[i].sk, 100);
+		}
+		if (max_msgsize > DEFAULT_MAX_WINDOW) {
+			if (setsockopt(poll_sks[i].sk, SOL_SOCKET, SO_RCVBUF,
+				&max_msgsize, sizeof(max_msgsize)) < 0) {
+				perror("setsockopt(SO_RCVBUF)");
+				exit(1);
+			}
+		} 	
+
+		if (tosend) {
+			if ((poll_sks[i].assoc_state = (_assoc_state *)malloc(
+				sizeof(_assoc_state) * tosend)) == NULL) {
+				printf("Can't allocate memory.\n");
+				goto clean_up;
+			}
+			memset(poll_sks[i].assoc_state, 0,
+				sizeof(_assoc_state) * tosend);
+		}
+
+		if (poll_sks[i].sk > max_fd) {
+			max_fd = poll_sks[i].sk;
+		}
+	}
+
+	size = howmany(max_fd + 1, NFDBITS) * sizeof(fd_mask);
+	if ((ibitsp = (fd_set *)malloc(size)) == NULL) {
+		printf("Can't allocate memory.\n");
+		goto clean_up;
+	}
+	if ((obitsp = (fd_set *)malloc(size)) == NULL) {
+		printf("Can't allocate memory.\n");
+		goto clean_up;
+	}
+	if ((xbitsp = (fd_set *)malloc(size)) == NULL) {
+		printf("Can't allocate memory.\n");
+		goto clean_up;
+	}
+
+	memset(ibitsp, 0, size);
+	memset(obitsp, 0, size);
+	memset(xbitsp, 0, size);
+
+
+        /* Initialize inmessage with enough space for DATA... */
+        memset(&inmessage, 0, sizeof(inmessage));
+        if ((iov.iov_base = malloc(REALLY_BIG)) == NULL) {
+		fprintf(stderr, "\n\t\t*** malloc not enough memory!!! ***\n");
+		goto clean_up;
+	}
+	iov.iov_len = REALLY_BIG;
+	inmessage.msg_iov = &iov;
+	inmessage.msg_iovlen = 1;
+	/* or a control message.  */
+	inmessage.msg_control = incmsg;
+	inmessage.msg_controllen = sizeof(incmsg);
+
+	/* Set up the remote port number per association for output.  */
+	for (i = 0; i < sks; i++) {
+		as = poll_sks[i].assoc_state;
+		for (j = 0; j < tosend; j++) {
+			as[j].rem_port = remote_port + j;
+		}
+	}
+
+	while (!done) {
+
+		for (i = 0; i < sks; i++) {
+			FD_SET(poll_sks[i].sk, ibitsp);
+			FD_SET(poll_sks[i].sk, obitsp);
+			FD_SET(poll_sks[i].sk, xbitsp);
+		}
+		if ((error = select(max_fd + 1, ibitsp, obitsp, xbitsp,
+			(struct timeval *)0)) < 0) {
+			fprintf(stderr, "\n\t\t*** select() failed ");
+			fprintf(stderr, "with error: %s\n\n",
+				strerror(errno));
+			fflush(stdout);
+			goto clean_up;
+		}
+		
+		for (i = 0; i < sks; i++) {
+			/* Is there anything to read from the socket?  */
+			if (listeners && FD_ISSET(poll_sks[i].sk, ibitsp)) {
+
+				FD_CLR(poll_sks[i].sk, ibitsp);
+				error = recvmsg(poll_sks[i].sk, &inmessage,
+					MSG_WAITALL);
+				if (error < 0) {
+					fprintf(stderr,
+						"\n\t\t*** recvmsg: %s ***\n\n",
+						strerror(errno));
+					fflush(stdout);
+					goto clean_up;
+				}
+				else if (error == 0) {
+					printf("\n\t\trecvmsg() returned ");
+				       	printf("0 !!!!\n");
+					fflush(stdout);
+				}
+
+				print_message(poll_sks[i].sk, &inmessage,
+					error);
+
+				inmessage.msg_control = incmsg;
+				inmessage.msg_controllen = sizeof(incmsg);
+				iov.iov_len = REALLY_BIG;
+			}
+			
+			/* Is this socket writeable?  */
+			if (tosend && FD_ISSET(poll_sks[i].sk, obitsp)) {
+
+				FD_CLR(poll_sks[i].sk, obitsp);
+
+				/* Pick an association.  */
+				assoc_i = next_assoc(i, poll_sks[i].assoc_i,
+						assoc_pattern);
+				if (assoc_i < 0) {
+					/* No work to do on any associations.
+					 * We are probably done. */
+					if (!listeners) {
+						done = 1;
+					}
+					continue;
+				}
+				poll_sks[i].assoc_i = assoc_i;
+
+				as = poll_sks[i].assoc_state;
+				n_msg_size = next_msg_size(as[assoc_i].msg_cnt);
+				n_order = as[assoc_i].order_state =
+					next_order(as[assoc_i].order_state,
+					order_pattern);
+				n_stream = as[assoc_i].stream_state =
+					next_stream(as[assoc_i].stream_state,
+					stream_pattern);
+
+				/* Set the destination port.  */
+				if (s_rem.ss_family == AF_INET6)
+					( (struct sockaddr_in6 *)&s_rem)->
+						sin6_port =
+						htons(as[assoc_i].rem_port);
+				else
+					( (struct sockaddr_in *)&s_rem)->
+						sin_port =
+						htons(as[assoc_i].rem_port);
+
+				/* Send a message thru the association.  */
+				if (send_r(poll_sks[i].sk, n_stream, n_order,
+					n_msg_size, assoc_i) < 0) {
+					/* Don't increment counter if there
+					 * is a problem of sending.
+					 */
+					continue;
+				}
+
+				/* Increment counters. */
+				if (++as[assoc_i].msg_cnt >= MSG_CNT) {
+					as[assoc_i].msg_cnt = 0;
+				}
+				if (++as[assoc_i].msg_sent >=
+					repeat_count) {
+					fprintf(stderr, "Association #%d in ",
+						assoc_i);
+					fprintf(stderr, "sk=%d has ",
+						poll_sks[i].sk);
+					fprintf(stderr, "completed %d msg as ",
+						as[assoc_i].msg_sent);
+					fprintf(stderr, "cycle %d.\n",
+						++as[assoc_i].cycle);
+
+					/* In the mixed mode, -x not only
+					 * specify the longer repeat cycle,
+					 * but it also mean to run the test
+					 * forever.
+					 */
+					if (xflag) {
+						as[assoc_i].msg_sent = 0;
+					}
+
+				}
+
+			}
+		}
+	}
+
+clean_up:
+	for (i = 0; i < sks; i++) {
+		close(poll_sks[i].sk);
+		if (poll_sks[i].assoc_state) {
+			free(poll_sks[i].assoc_state);
+		}
+	}
+
+	if (ibitsp) free(ibitsp);
+	if (obitsp) free(obitsp);
+	if (xbitsp) free(xbitsp);
+
+	if (iov.iov_base) free(iov.iov_base);
+
+} /* mixed_mode_test() */
+
+void start_test(int role)
+{
+	int sk;
+	int i = 0;
+	
+	DEBUG_PRINT(DEBUG_NONE, "\nStarting tests...\n");
+
+	repeat_count = repeat;
+
+
+	if (MIXED == role) {
+		repeat_count = repeat_count * msg_cnt;  /* Repeat per assoc. */
+		mixed_mode_test();
+		return;
+	}
+
+	sk = socket_r();
+	if (sk < 0) {
+		DEBUG_PRINT(DEBUG_NONE, "\nSocket create err %d\n", errno);
+		return;
+	}
+
+	if (bind_r(sk, &s_loc) == -1) {
+		DEBUG_PRINT(DEBUG_NONE, "\nSocket bind err %d\n", errno);
+		return;
+	}
+
+	/* Do we need to do bindx() to add any additional addresses? */
+	if (bindx_add_addrs)
+		bindx_r(sk, bindx_add_addrs, bindx_add_count,
+			   SCTP_BINDX_ADD_ADDR);
+
+	if (role == SERVER) {
+		listen_r(sk, 100);
+	} else {
+		if (socket_type == SOCK_STREAM && connectx_count == 0)
+			connect_r(sk, (struct sockaddr *)&s_rem, r_len);
+
+		if (connectx_count != 0)
+			connectx_r(sk, connectx_addrs, connectx_count);
+	}
+
+	if (!debug_level) {
+		printf("     ");
+	}
+
+	for(i = 0; i < repeat_count; i++) {
+		
+		if (role == SERVER) {
+			DEBUG_PRINT(DEBUG_NONE,
+				    "Server: Receiving packets.\n");
+			server(sk);
+		} else {
+			DEBUG_PRINT(DEBUG_NONE,
+				    "Client: Sending packets.(%d/%d)\n",
+		    		    i+1, repeat_count);
+			client(sk);
+		}
+
+		fflush(stdout);
+	}
+
+	close_r(sk);
+
+} /* start_test() */
+
+int
+main(int argc, char *argv[])
+{
+	int c;
+	char *interface = NULL;
+	struct sockaddr_in *t_addr;
+	struct sockaddr_in6 *t_addr6;
+	struct sockaddr *tmp_addrs = NULL;
+	
+        /* Parse the arguments.  */
+        while ((c = getopt(argc, argv, ":H:L:P:S:a:h:p:c:d:lm:sx:X:o:t:M:r:w:Di:TB:C:O:")) >= 0 ) {
+
+                switch (c) {
+		case 'H':
+			local_host = optarg;
+			break;
+		case 'L':
+			role = MIXED;
+			listeners = atoi(optarg);
+			if (listeners > MAX_POLL_SKS) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 'P':
+			local_port = atoi(optarg);
+			break;
+		case 'S':
+			role = MIXED;
+			tosend = atoi(optarg);
+			if (tosend > MAX_POLL_SKS) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 'a':
+			assoc_pattern = atoi(optarg);
+			if (assoc_pattern <  ASSOC_PATTERN_SEQUENTIAL
+			    || assoc_pattern > ASSOC_PATTERN_RANDOM ) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 'h':
+			remote_host = optarg;
+			break;
+		case 'D':
+			drain = 1;
+			do_exit = 0;
+			break;
+		case 'p':
+			remote_port = atoi(optarg);
+			break;
+		case 's':
+			if (role != NOT_DEFINED) {
+				printf("%s: only -s or -l\n", argv[0]);
+				usage(argv[0]);
+				exit(1);
+			}
+			role = CLIENT;
+			break;
+		case 'l':
+			if (role != NOT_DEFINED) {
+				printf("%s: only -s or -l\n", argv[0]);
+				usage(argv[0]);
+				exit(1);
+			}
+			role = SERVER;
+			break;
+		case 'd':
+			debug_level = atoi(optarg);
+			if (debug_level < DEBUG_NONE
+			    || debug_level > DEBUG_MAX) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 'x':
+			repeat = atoi(optarg);
+			if (!repeat) {
+				xflag = 1;
+				repeat = BIG_REPEAT;
+			}
+			break;
+		case 'X':
+			msg_cnt = atoi(optarg);
+			if ((msg_cnt <= 0) || (msg_cnt > MSG_CNT)) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 'c':
+			test_case = atoi(optarg);
+			if (test_case > NCASES) {
+				usage(argv[0]);
+				exit(1);
+			}
+			if (test_case < 0) {
+				size_arg = -test_case;
+			}
+			
+			break;
+		case 'o':
+			order_pattern = atoi(optarg);
+			if (order_pattern <  ORDER_PATTERN_UNORDERED
+			    || order_pattern  > ORDER_PATTERN_RANDOM ) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 'O':
+			timetolive = atoi(optarg);
+			if (timetolive < 0) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 't':
+			stream_pattern = atoi(optarg);
+			if (stream_pattern <  STREAM_PATTERN_SEQUENTIAL
+			    || stream_pattern > STREAM_PATTERN_RANDOM ) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 'M':
+			max_stream = atoi(optarg);
+			if (max_stream <  0
+			    || max_stream >= (1<<16)) {
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
+		case 'r':
+			seed = atoi(optarg);
+			break;
+		case 'm':
+			max_msgsize = atoi(optarg);
+#if 0
+			if ((max_msgsize < DEFAULT_MIN_WINDOW) ||
+			    (max_msgsize > 65515)) {
+				usage(argv[0]);
+				exit(1);
+			}
+#endif
+			break;
+		case 'i':
+			interface = optarg;
+			if_index = if_nametoindex(interface);
+			if (!if_index) {
+				printf("Interface %s unknown\n", interface);
+				exit(1);
+			}
+			break;
+		case 'T':
+			socket_type = SOCK_STREAM;
+			break;
+		case 'B':
+			tmp_addrs = append_addr(optarg, bindx_add_addrs,
+						&bindx_add_count);
+			if (NULL == tmp_addrs) {
+				fprintf(stderr, "No memory to add ");
+				fprintf(stderr, "%s\n", optarg);
+				exit(1);
+			}
+			bindx_add_addrs = tmp_addrs;
+			break;
+		case 'C':
+			tmp_addrs = append_addr(optarg, connectx_addrs,
+						&connectx_count);
+			if (NULL == tmp_addrs) {
+				fprintf(stderr, "No memory to add ");
+				fprintf(stderr, "%s\n", optarg);
+				exit(1);
+			}
+			connectx_addrs = tmp_addrs;
+			break;
+		case '?':
+		default:
+			usage(argv[0]);
+			exit(0);
+		}
+	} /* while() */
+
+	if (NOT_DEFINED == role) {
+		usage(argv[0]);
+		exit(1);
+	}
+
+
+	if (SERVER == role && NULL == local_host && remote_host != NULL) {
+		fprintf (stderr, "%s: Server needs local address, "
+			 "not remote address\n", argv[0]);
+		usage(argv[0]);
+		exit(1);
+	}
+	if (CLIENT == role && NULL == remote_host && connectx_count == 0) {
+		fprintf (stderr, "%s: Client needs at least remote address "
+			 "& port\n", argv[0]);
+		usage(argv[0]);
+		exit(1);
+	}
+	if (MIXED == role) {
+		if (listeners && NULL == local_host) {
+			fprintf (stderr, "%s: Servers need local address\n",
+				argv[0]);
+			usage(argv[0]);
+			exit(1);
+		}
+		if (tosend && NULL == remote_host) {
+			fprintf (stderr, "%s: Clients need remote address ",
+				argv[0]);
+			fprintf (stderr, "& port\n");
+			usage(argv[0]);
+			exit(1);
+		}
+	}
+
+	if (optind < argc) {
+                fprintf(stderr, "%s: non-option arguments are illegal: ",
+                        argv[0]);
+                while (optind < argc)
+                        fprintf(stderr, "%s ", argv[optind++]);
+                fprintf (stderr, "\n");
+                usage(argv[0]);
+                exit(1);
+	}
+
+	if (remote_host != NULL && connectx_count != 0) {
+		fprintf(stderr, "%s: You can not provide both -h and -C options.\n",
+			argv[0]);
+		usage(argv[0]);
+		exit(1);
+	}
+
+	if (remote_host != NULL && remote_port != 0) {
+		struct addrinfo *res;
+		int error;
+		char *host_s, *serv_s;
+
+		if ((host_s = malloc(NI_MAXHOST)) == NULL) {
+			fprintf(stderr, "\n*** host_s malloc failed!!! ***\n");
+			exit(1);
+		}
+		if ((serv_s = malloc(NI_MAXSERV)) == NULL) {
+			fprintf(stderr, "\n*** serv_s malloc failed!!! ***\n");
+			exit(1);
+		}
+
+		error = getaddrinfo(remote_host, 0, NULL, &res);
+		if (error) {
+			printf("%s.\n", gai_strerror(error));
+			usage(argv[0]);
+			exit(1);
+		}
+
+		switch (res->ai_family) {
+			case AF_INET:
+				t_addr = (struct sockaddr_in *)&s_rem;
+
+				memcpy(t_addr, res->ai_addr,
+				       res->ai_addrlen);
+				t_addr->sin_family = res->ai_family;
+				t_addr->sin_port = htons(remote_port);
+
+				r_len = res->ai_addrlen;
+
+#ifdef __FreeBSD__
+				t_addr->sin_len = r_len;
+#endif
+				break;
+			case AF_INET6:
+
+				t_addr6 = (struct sockaddr_in6 *)&s_rem;
+				
+				memcpy(t_addr6, res->ai_addr,
+				       res->ai_addrlen);
+				t_addr6->sin6_family = res->ai_family;
+				t_addr6->sin6_port = htons(remote_port);
+				if (interface)
+					t_addr6->sin6_scope_id =
+						if_nametoindex(interface);
+
+				r_len = res->ai_addrlen;
+
+#ifdef __FreeBSD__
+				t_addr6->sin6_len = r_len;
+#endif
+				break;
+		}
+
+		getnameinfo((struct sockaddr *)&s_rem, r_len, host_s,
+			    NI_MAXHOST, serv_s, NI_MAXSERV, NI_NUMERICHOST);
+			
+		DEBUG_PRINT(DEBUG_MAX, "remote:addr=%s, port=%s, family=%d\n",
+			    host_s, serv_s, res->ai_family);
+
+		freeaddrinfo(res);
+        }
+
+	if (connectx_count != 0) {
+		switch (connectx_addrs->sa_family) {
+		case AF_INET:
+			t_addr = (struct sockaddr_in *)&s_rem;
+			r_len = sizeof(struct sockaddr_in);
+			memcpy(t_addr, connectx_addrs, r_len);
+			t_addr->sin_port = htons(remote_port);
+			break;
+		case AF_INET6:
+			t_addr6 = (struct sockaddr_in6 *)&s_rem;
+			r_len = sizeof(struct sockaddr_in6);
+			memcpy(t_addr6, connectx_addrs, r_len);
+			t_addr6->sin6_port = htons(remote_port);
+			break;
+		}
+	}
+
+	if (local_host != NULL) {
+		struct addrinfo *res;
+		int error;
+		char *host_s, *serv_s;
+		struct sockaddr_in *t_addr;
+		struct sockaddr_in6 *t_addr6;
+
+		if ((host_s = malloc(NI_MAXHOST)) == NULL) {
+			fprintf(stderr, "\n*** host_s malloc failed!!! ***\n");
+			exit(1);
+		}
+		if ((serv_s = malloc(NI_MAXSERV)) == NULL) {
+			fprintf(stderr, "\n*** serv_s malloc failed!!! ***\n");
+			exit(1);
+		}
+
+		if (strcmp(local_host, "0") == 0)
+			local_host = "0.0.0.0";
+
+		error = getaddrinfo(local_host, 0, NULL, &res);
+		if (error) {
+			printf("%s.\n", gai_strerror(error));
+			usage(argv[0]);
+			exit(1);
+		}
+
+		switch (res->ai_family) {
+			case AF_INET:
+				t_addr = (struct sockaddr_in *)&s_loc;
+				memcpy(t_addr, res->ai_addr,
+				       res->ai_addrlen);
+				t_addr->sin_family = res->ai_family;
+				t_addr->sin_port = htons(local_port);
+
+				l_len = res->ai_addrlen;
+
+#ifdef __FreeBSD__
+				t_addr->sin_len = l_len;
+#endif
+				break;
+			case AF_INET6:
+				t_addr6 = (struct sockaddr_in6 *)&s_loc;
+
+				memcpy(t_addr6, res->ai_addr,
+				       res->ai_addrlen);
+				t_addr6->sin6_family = res->ai_family;
+				t_addr6->sin6_port = htons(local_port);
+				if (interface)
+					t_addr6->sin6_scope_id =
+						if_nametoindex(interface);
+
+				l_len = res->ai_addrlen;
+
+#ifdef __FreeBSD__
+				t_addr6->sin6_len = l_len;
+#endif
+				break;
+		}
+
+		error = getnameinfo((struct sockaddr *)&s_loc, l_len, host_s,
+			    NI_MAXHOST, serv_s, NI_MAXSERV, NI_NUMERICHOST);
+
+		if (error)
+			printf("%s..\n", gai_strerror(error));
+
+		DEBUG_PRINT(DEBUG_MAX, "local:addr=%s, port=%s, family=%d\n",
+			    host_s, serv_s, res->ai_family);
+
+		freeaddrinfo(res);
+        }
+
+
+	/* A half-hearted attempt to seed rand() */
+	if (seed == 0 ) {
+		seed = time(0);
+		DEBUG_PRINT(DEBUG_NONE, "seed = %d\n", seed);	
+	}
+	
+	srand(seed);
+
+	/* Let the testing begin. */
+	start_test(role);
+
+	return 0;
+
+} /*  main() */