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() */