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/.gitignore b/src/apps/.gitignore
new file mode 100644
index 0000000..a7ba019
--- /dev/null
+++ b/src/apps/.gitignore
@@ -0,0 +1,10 @@
+bindx_test
+myftp
+nagle_rcv
+nagle_snd
+peel_client
+peel_server
+sctp_darn
+sctp_status
+sctp_test
+sctp_xconnect
diff --git a/src/apps/Makefile.am b/src/apps/Makefile.am
new file mode 100644
index 0000000..7e32306
--- /dev/null
+++ b/src/apps/Makefile.am
@@ -0,0 +1,42 @@
+# Include these two in all the Makefile.am's!!!
+include $(top_srcdir)/Makefile.vars
+include $(top_srcdir)/Makefile.rules
+include $(top_srcdir)/Makefile.dirs
+
+# General compilation flags
+AM_CPPFLAGS = -I. -I$(top_builddir)/src/include -I$(top_srcdir)/src/testlib \
+ -g -O2 -fno-strict-aliasing -Wall -Wstrict-prototypes \
+ -Wimplicit-function-declaration
+
+AM_LDFLAGS =
+
+LDADD = $(top_builddir)/src/testlib/libsctputil.la \
+ $(top_builddir)/src/lib/libsctp.la
+
+# programs to be installed with the distriubution
+bin_PROGRAMS = sctp_darn sctp_test sctp_status
+
+# Test programs and libraries to build
+noinst_PROGRAMS = bindx_test nagle_snd nagle_rcv myftp sctp_xconnect \
+ peel_server peel_client sctp_test sctp_status
+
+$(top_builddir)/src/lib/libsctp.la:
+ $(MAKE) -C $(top_builddir)/src/lib libsctp.la
+
+$(top_builddir)/src/testlib/libsctputil.la:
+ $(MAKE) -C $(top_builddir)/src/testlib libsctputil.la
+
+# Specifying the sources
+bindx_test_SOURCES = bindx_test.c
+sctp_darn_SOURCES = sctp_darn.c sctp_darn.h
+sctp_test_SOURCES = sctp_test.c
+sctp_status_SOURCES = sctp_status.c
+nagle_rcv_SOURCES = nagle_rcv.c
+nagle_snd_SOURCES = nagle_snd.c
+myftp_SOURCES = myftp.c
+sctp_xconnect_SOURCES = sctp_xconnect.c
+peel_server_SOURCES = peel_server.c
+peel_client_SOURCES = peel_client.c
+
+# Tutorials
+pkgdoc_DATA = sctp_darn.c sctp_darn.h sctp_test.c sctp_status.c
diff --git a/src/apps/bindx_test.c b/src/apps/bindx_test.c
new file mode 100644
index 0000000..ff3de86
--- /dev/null
+++ b/src/apps/bindx_test.c
@@ -0,0 +1,134 @@
+ /* -*-c-*-
+ **
+ ** sctp-tools: Another bindx test.
+ **
+ ** $Id: bindx_test.c,v 1.1.1.1 2002/08/06 22:31:05 inaky Exp $
+ **
+ ** Distributed under the terms of the GPL v2.0 as described in
+ ** $top_srcdir/COPYING.
+ **
+ ** (C) Copyright IBM Corp. 2003
+ ** (C) 2002 Intel Corporation
+ ** Iñaky Pérez-González <inaky.perez-gonzalez@intel.com>:
+ ** Sridhar Samudrala <sri@us.ibm.com>
+ */
+
+#define _GNU_SOURCE /* GNU extensions */
+
+#include <stdlib.h> /* malloc() */
+#include <arpa/inet.h> /* inet_pton() */
+#include <errno.h>
+#include <sys/socket.h> /* socket() */
+#include <stdio.h> /* fprintf */
+#include <netinet/in.h> /* sockaddr_in */
+#include <unistd.h> /* close() */
+#include <string.h> /* strchr() */
+#include <netinet/sctp.h> /* bindx() */
+
+ /* Global stuff */
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+
+ /*! Main function: initialize, setup, run the main loop
+ **
+ **
+ */
+
+int main (int argc, char **argv)
+{
+ void *addr_buf, *buf_ptr;
+ void *addr_buf_size = 0;
+ size_t addrs, cnt;
+ int sd, result, port;
+ int domain = PF_INET6;
+
+ if (argc < 3) {
+ fprintf(stderr,
+ "Usage: bindx_test PORT IPADDR1 [IPADDR2 [...]]\n");
+ return 1;
+ }
+
+ port = atoi(argv[1]);
+ printf("bindx_test: INFO: Port is %d\n", port);
+
+ /* Allocate the maximum space for the specified no. of addresses.
+ * Assume all of them are v6 addresses.
+ */
+ addr_buf = malloc((argc -2) * sizeof(struct sockaddr_in6));
+ if (addr_buf == NULL) {
+ perror("bindx_test: ERROR: addr buf allocation failed");
+ return 1;
+ }
+
+ /* Get the addresses from the cmd line */
+ addrs = 0; /* healthy address iterator [and total counter] */
+ cnt = 2; /* argument iterator */
+ buf_ptr = addr_buf;
+ while (cnt < argc) {
+ printf("bindx_test: INFO: Arg %zu: %s", cnt, argv[cnt]);
+ fflush(stderr);
+ if (strchr(argv[cnt], ':')) {
+ struct sockaddr_in6 *sa6;
+
+ sa6 = (struct sockaddr_in6 *)buf_ptr;
+ printf(" IPv6 address number %zu", addrs);
+ sa6->sin6_family = AF_INET6;
+ sa6->sin6_port = port;
+ if (inet_pton(AF_INET6, argv[cnt], &sa6->sin6_addr)) {
+ addrs++;
+ addr_buf_size += sizeof(struct sockaddr_in6);
+ buf_ptr += sizeof(struct sockaddr_in6);
+ } else
+ printf(" error");
+ } else if (strchr(argv[cnt], '.')) {
+ struct sockaddr_in *sa;
+
+ domain = PF_INET;
+ sa = (struct sockaddr_in *)buf_ptr;
+ printf (" IPv4 address number %zu", addrs);
+ sa->sin_family = AF_INET;
+ sa->sin_port = port;
+ if (inet_pton (AF_INET, argv[cnt], &sa->sin_addr)) {
+ addrs++;
+ addr_buf_size += sizeof(struct sockaddr_in);
+ buf_ptr += sizeof(struct sockaddr_in);
+ } else
+ printf (" error");
+ } else
+ printf (" Unknown");
+ putchar ('\n');
+ cnt++;
+ }
+
+ printf ("bindx_test: INFO: Got %zu addrs\n", addrs);
+
+ /* Create the socket */
+ sd = socket(domain, SOCK_SEQPACKET, IPPROTO_SCTP);
+ if (sd == -1) {
+ perror("bindx_test: ERROR: Cannot open socket");
+ return 1;
+ }
+
+ /* add all */
+ result = sctp_bindx(sd, (struct sockaddr *)addr_buf, addrs,
+ SCTP_BINDX_ADD_ADDR);
+ if (result == -1)
+ perror("bindx_test: ERROR: bindx addition error");
+ else {
+ printf("bindx_test: OK: bindx address addition\n");
+
+ /* remove all but the last */
+ result = sctp_bindx(sd, (struct sockaddr *)addr_buf, addrs-1,
+ SCTP_BINDX_REM_ADDR);
+ if (result == -1)
+ perror("bindx_test: ERROR: bindx address removal");
+ else
+ printf("bindx_test: OK: bindx address removal\n");
+ }
+
+ close(sd);
+ free(addr_buf);
+ return result;
+}
diff --git a/src/apps/myftp.c b/src/apps/myftp.c
new file mode 100644
index 0000000..64fa3f2
--- /dev/null
+++ b/src/apps/myftp.c
@@ -0,0 +1,413 @@
+/* myftp - simple file transfer over sctp testing tool.
+ * Copyright (c) 2002 Intel Corp.
+ *
+ * This file is part of the LKSCTP kernel Implementation. This
+ * is a submission by Xingang Guo from the Intel Corporation while
+ * participating on the LKSCTP project.
+ *
+ * The SCTP implementation 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.
+ *
+ * The SCTP implementation 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 <sctp-developers-list@cig.mot.com>
+ *
+ * Or submit a bug report through the following website:
+ * http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ * Xingang Guo <xingang.guo@intel.com>
+ * Jon Grimm <jgrimm@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h> /* for sockaddr_in */
+#include <errno.h>
+#include <netinet/sctp.h>
+
+#define BUFSIZE 1024
+static char buffer[BUFSIZE];
+#define DUMP_CORE { char *diediedie = 0; *diediedie = 0; }
+
+typedef enum { COMMAND_NONE, COMMAND_RECV, COMMAND_SEND } command_t;
+
+/* These are the global options. */
+#define MAX_NUM_HOST 5
+static char *local_host[MAX_NUM_HOST];
+static int num_local_host = 0;
+static int local_port = 4444;
+
+static int buffer_size = BUFSIZE;
+static char *remote_host = NULL;
+static int remote_port = 4444;
+static command_t command = COMMAND_NONE;
+static char *filename = NULL;
+static int interactive = 0;
+static unsigned long delay = 0;
+static int verbose = 0;
+
+static void
+usage(char *argv0)
+{
+ fprintf(stderr, "Usage: %s [options]\n",argv0);
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, "\t--local, -H <hostname> Specify local interface\n");
+ fprintf(stderr, "\t--local-port, -P <port> Specify local port (default 4444)\n");
+ fprintf(stderr, "\t--remote, -h <hostname> Specify interface on remote host\n");
+ fprintf(stderr, "\t--remote-port, -p <port> Specify remote port (default 4444)\n");
+ fprintf(stderr, "\t--listen, -l Work in receiving mode\n");
+ fprintf(stderr, "\t--send, -s Work in sending mode\n");
+ fprintf(stderr, "\t--file, -f <filename> File to read or write,\n");
+ fprintf(stderr, "\t--buffer, -b <size> Buffer size. (default 1024 bytes)\n");
+ fprintf(stderr, "\t by default use standard input/output.\n");
+ fprintf(stderr, "\t--quick, -q Send packets continueously,\n");
+ fprintf(stderr, "\t do not wait for <ENTER> key. Default wait.\n");
+ fprintf(stderr, "\t--delay, -d <usec> Delay between consecutive sends (see --quick)\n");
+ fprintf(stderr, "\t--verbose, -v In verbose mode, display the progress.\n");
+ fprintf(stderr, "\n\t--help, Print this message.\n\n");
+} /* usage() */
+
+static int parse_arguments(int argc, char *argv[])
+{
+ int option_index = 0;
+ int c;
+ static struct option long_options[] = {
+ {"local", 1, 0, 1},
+ {"local-port", 1, 0, 2},
+ {"remote", 1, 0, 3},
+ {"remote-port", 1, 0, 4},
+ {"file", 1, 0, 5},
+ {"delay", 1, 0, 6},
+ {"buffer", 1, 0, 7},
+ {"listen", 0, 0, 10},
+ {"send", 0, 0, 11},
+ {"quick", 0, 0, 12},
+ {"verbose", 0, 0, 13},
+ {"help", 0, 0, 99},
+ {0, 0, 0, 0}
+ };
+
+ /* Parse the arguments. */
+ while (1) {
+ c = getopt_long(argc, argv, "H:P:h:p:f:d:b:qlsv",long_options,&option_index);
+ if (c == -1) break;
+
+ switch (c) {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg) printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+ case 1: /* local host */
+ case 'H':
+ local_host[num_local_host++] = optarg;
+ break;
+ case 2: /* local port */
+ case 'P':
+ local_port = atoi(optarg);
+ break;
+ case 3: /* remote host */
+ case 'h':
+ remote_host = optarg;
+ break;
+ case 4: /* remote port */
+ case 'p':
+ remote_port = atoi(optarg);
+ break;
+ case 5:
+ case 'f':
+ filename = optarg;
+ break;
+
+ case 6:
+ case 'd':
+ delay = strtoul(optarg,NULL,10);
+ printf("delay is %ld usec\n",delay);
+ break;
+
+ case 7:
+ case 'b':
+ buffer_size = atoi(optarg);
+ if ( buffer_size > BUFSIZE ) {
+ buffer_size = BUFSIZE;
+ fprintf(stderr,"Warning, buffer size too large, set to %d\n",buffer_size);
+ }
+ break;
+
+ case 12:
+ case 'q': interactive = 0; break;
+
+ case 13:
+ case 'v': verbose = 1; break;
+ /* COMMANDS */
+ case 10: /* listen */
+ case 'l':
+ if (command) {
+ fprintf(stderr, "%s: pick ONE of listen or send\n", argv[0]);
+ return 1;
+ }
+ else command = COMMAND_RECV;
+ break;
+
+ case 11: /* send */
+ case 's':
+ if (command) {
+ fprintf(stderr, "%s: pick ONE of listen or send\n", argv[0]);
+ return 2;
+ } else command = COMMAND_SEND;
+ break;
+
+ case '?':
+ case 99:
+ usage(argv[0]);
+ return 3;
+ break;
+
+ default:
+ printf ("%s: unrecognized option 0%c\n", argv[0], c);
+ usage(argv[0]);
+ return 4;
+ }
+ }
+
+ 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]);
+ return 5;
+ }
+
+
+ if (0 == num_local_host) {
+ fprintf(stderr, "%s: You MUST provide a local host.\n", argv[0]);
+ usage(argv[0]);
+ return 6;
+ }
+
+ if ( filename == NULL && command == COMMAND_SEND)
+ fprintf(stderr,"%s: Use standard input to send\n",argv[0]);
+
+ if ( filename == NULL && command == COMMAND_RECV )
+ fprintf(stderr,"%s: Use standard output to write\n",argv[0]);
+
+ return 0;
+} /* parse_arguments() */
+
+static void
+emsg(char *prog,char *s)
+{
+ if ( prog != NULL ) fprintf(stderr,"%s: ",prog);
+ perror(s);
+ fflush(stdout);
+ //DUMP_CORE;
+
+ exit(-1);
+}
+
+static int build_endpoint(char *argv0)
+{
+ int retval,i;
+
+ /* Create the local endpoint. */
+ if ( (retval = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0 ) {
+ emsg(argv0,"socket");
+ exit(retval);
+ }
+
+ for ( i = 0;i < num_local_host;i++ ) {
+ struct hostent *hst;
+ struct sockaddr_in laddr;
+
+ memset(&laddr, 0, sizeof(laddr));
+ /* Get the transport address for the local host name. */
+ fprintf(stderr,"Hostname %d is %s\n",i+1,local_host[i]);
+ if ( (hst = gethostbyname(local_host[i])) == NULL ) {
+ fprintf(stderr, "%s: bad hostname: %s\n", argv0, local_host[i]);
+ exit(1);
+ }
+ memcpy(&laddr.sin_addr, hst->h_addr_list[0],sizeof(laddr.sin_addr));
+ laddr.sin_port = htons(local_port);
+ laddr.sin_family = AF_INET;
+
+ /* Bind this socket to the test port. */
+ if ( bind(retval, (struct sockaddr *)&laddr, sizeof(laddr)) ) {
+ emsg(argv0,"bind");
+ exit(-1);
+ }
+ }
+
+ fprintf(stderr,"Endpoint built.\n");
+
+ return retval;
+} /* build_endpoint() */
+
+/* Convenience structure to determine space needed for cmsg. */
+typedef union {
+ struct sctp_initmsg init;
+ struct sctp_sndrcvinfo sndrcvinfo;
+} _sctp_cmsg_data_t;
+
+
+/* Listen on the socket, printing out anything that arrives. */
+static void
+command_recv(char *argv0, int sk)
+{
+ struct msghdr inmessage;
+ char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
+ struct iovec iov;
+ int ret;
+ int fd;
+ int ct;
+
+ if (listen(sk, 1) == -1)
+ emsg(argv0, "listen");
+ /* Initialize inmessage with enough space for DATA... */
+ memset(&inmessage, 0, sizeof(inmessage));
+ iov.iov_base = buffer;
+ iov.iov_len = buffer_size;
+ inmessage.msg_iov = &iov;
+ inmessage.msg_iovlen = 1;
+ /* or a control message. */
+ inmessage.msg_control = incmsg;
+ inmessage.msg_controllen = sizeof(incmsg);
+
+ /* creat a file */
+ if ( filename == NULL ) fd = 1;
+ else if ( (fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE)) == -1 )
+ emsg(argv0,"open");
+
+ fprintf(stderr,"%s Receiving...\n", argv0);
+ /* Get the messages sent */
+ ct = 0;
+ while ( (ret = recvmsg(sk, &inmessage, MSG_WAITALL)) >= 0 ) {
+ if ( verbose )
+ fprintf(stderr,"%s-%d received %d bytes\n",
+ argv0, ++ct, ret);
+ if ( !(inmessage.msg_flags & MSG_NOTIFICATION) ) {
+ //printf("%s write %d bytes\n",argv0,ret);
+ if ( write(fd,buffer,ret) != ret ) emsg(argv0,"write");
+ } else {
+ union sctp_notification *sn;
+ sn = (union sctp_notification *)iov.iov_base;
+ if ((sn->sn_header.sn_type == SCTP_ASSOC_CHANGE) &&
+ (sn->sn_assoc_change.sac_state
+ == SCTP_SHUTDOWN_COMP))
+ break;
+ }
+
+ }
+
+ if ( ret < 0 ) emsg(argv0,"recvmsg");
+
+ close(fd);
+ close(sk);
+} /* command_recv() */
+
+/* Read lines from stdin and send them to the socket. */
+static void
+command_send(char *argv0, int sk)
+{
+ struct msghdr outmsg;
+ struct iovec iov;
+ struct hostent *hst;
+ struct sockaddr_in remote_addr;
+ int fd;
+ int msglen;
+ int ct;
+
+ /* Set up the destination. */
+ hst = gethostbyname(remote_host);
+ if (hst == NULL || hst->h_length < 1) {
+ fprintf(stderr, "%s: bad hostname: %s\n", argv0, remote_host);
+ exit(1);
+ }
+ memcpy(&remote_addr.sin_addr, hst->h_addr_list[0], sizeof(remote_addr.sin_addr));
+ remote_addr.sin_port = htons(remote_port);
+ remote_addr.sin_family = AF_INET;
+
+ /* Initialize the message struct we use to pass messages to
+ * the remote socket.
+ */
+ iov.iov_base = buffer;
+ iov.iov_len = buffer_size;
+ outmsg.msg_iov = &iov;
+ outmsg.msg_iovlen = 1;
+ outmsg.msg_control = NULL;
+ outmsg.msg_controllen = 0;
+ outmsg.msg_name = &remote_addr;
+ outmsg.msg_namelen = sizeof(remote_addr);
+
+ /* open the file */
+ if ( filename == NULL ) fd = 0;
+ else if ( (fd = open(filename,O_RDONLY)) == -1 ) emsg(argv0,"open");
+
+ fprintf(stderr,"%s ready to send...\n", argv0);
+ ct = 0;
+ while ( (msglen = read(fd,buffer,buffer_size)) > 0 ) {
+ /* Send to our neighbor. */
+ iov.iov_len = msglen;
+ if ( sendmsg(sk, &outmsg, 0) != msglen ) emsg(argv0,"sendmsg");
+ if ( verbose ) fprintf(stderr,"%s-%d send %d bytes\n",argv0,++ct,msglen);
+ if ( interactive && fd != 1 )
+ getchar();
+ // no flow control? no problem, make it slow
+ else if ( delay > 0 )
+ usleep(delay);
+ }
+
+ close(fd);
+ close(sk);
+} /* command_send() */
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (( ret = parse_arguments(argc, argv) )) return ret;
+
+ switch(command) {
+ case COMMAND_NONE:
+ fprintf(stderr, "%s: Please specify a command.\n", argv[0]);
+ break;
+ case COMMAND_RECV:
+ command_recv(argv[0],build_endpoint(argv[0]));
+ break;
+ case COMMAND_SEND:
+ command_send(argv[0],build_endpoint(argv[0]));
+ break;
+ default:
+ fprintf(stderr, "%s: illegal command %d\n", argv[0], command);
+ } /* switch(command) */
+
+ return 0;
+}
diff --git a/src/apps/nagle_rcv.c b/src/apps/nagle_rcv.c
new file mode 100644
index 0000000..18e0945
--- /dev/null
+++ b/src/apps/nagle_rcv.c
@@ -0,0 +1,243 @@
+/* SCTP kernel Implementation
+ * (C) Copyright IBM Corp. 2002, 2003
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ *
+ * The SCTP implementation 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.
+ *
+ * The SCTP implementation 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
+ *
+ * Any bugs reported to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ *
+ * Written or modified by:
+ * Ardelle Fan <ardelle.fan@intel.com>
+ * Sridhar Samudrala <sri@us.ibm.com>
+ */
+
+/* This is a receiver for the performance test to verify Nagle's algorithm.
+ * It creates a socket, binds to a address specified as a parameter and
+ * goes into a receive loop waiting for 1,000,000 packets. Then it calculates
+ * the packet receive rate, i.e. packets/second.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.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 <sctputil.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <time.h>
+
+char *TCID = __FILE__;
+int TST_TOTAL = 1;
+int TST_CNT = 0;
+
+void
+usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s -H hostname [-P port]\n", progname);
+ fprintf(stderr, " -H, --local\t\t local hostname,\n");
+ fprintf(stderr, " -P, --local-port\t local port,\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int sk, i;
+ struct addrinfo *hst_res;
+ sockaddr_storage_t host;
+ sockaddr_storage_t msgname;
+ struct iovec iov;
+ struct msghdr inmessage;
+ char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))];
+ int error, pf_class;
+ char *big_buffer;
+ char *local_host = NULL;
+ int local_port = SCTP_TESTPORT_1;
+ char port_buffer[10];
+ int option_index = 0;
+ time_t from, to;
+ int bytes_received = 0;
+ int c;
+ static struct option long_options[] = {
+ {"local", 1, 0, 1},
+ {"local-port", 1, 0, 2},
+ {0, 0, 0, 0}
+ };
+
+ /* Rather than fflush() throughout the code, set stdout to
+ * be unbuffered.
+ */
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /* Parse the arguments. */
+ while (1) {
+ c = getopt_long (argc, argv, "H:P:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ printf("option %s", long_options[option_index].name);
+ if (optarg) {
+ printf(" with arg %s", optarg);
+ }
+ printf("\n");
+ break;
+ case 1: /* local host */
+ case 'H':
+ local_host = optarg;
+ break;
+ case 2: /* local port */
+ case 'P':
+ local_port = atoi(optarg);
+ break;
+ case '?':
+ usage(argv[0]);
+ exit(0);
+
+ default:
+ printf ("%s: unrecognized option 0%c\n", argv[0], c);
+ 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 (!local_host) {
+ fprintf(stderr, "%s: : option -H, --local is required\n",
+ argv[0]);
+ usage(argv[0]);
+ exit(1);
+ }
+
+ /* Set some basic values which depend on the address family. */
+ if (!strcmp(local_host, "0"))
+ local_host = "0.0.0.0";
+
+ snprintf(port_buffer, 10, "%d", local_port);
+ error = getaddrinfo(local_host, port_buffer, NULL, &hst_res);
+ if (error) {
+ fprintf(stderr, "%s: getaddrinfo failed: %s\n", argv[0], local_host);
+ exit(1);
+ }
+
+ pf_class = hst_res->ai_family;
+ switch (pf_class) {
+ case AF_INET:
+ case AF_INET6:
+ memcpy(&host.sa, hst_res->ai_addr, hst_res->ai_addrlen);
+ break;
+ default:
+ fprintf(stderr, "Invalid address type.\n");
+ exit(1);
+ break;
+ }
+
+ freeaddrinfo(hst_res);
+
+ /* Create the endpoint which will talk to nagle_snd. */
+ sk = test_socket(pf_class, SOCK_SEQPACKET, IPPROTO_SCTP);
+
+ /* Enable ASSOC_CHANGE and SNDRCVINFO notifications. */
+ test_enable_assoc_change(sk);
+
+ /* Bind the sockets to the test port. */
+ test_bind(sk, &host.sa, sizeof(host));
+
+ /* Mark sk as being able to accept new associations. */
+ test_listen(sk, 1);
+
+ printf("Listening on port:%d\n", local_port);
+
+ /* Initialize inmessage for receives. */
+ memset(&inmessage, 0, sizeof(inmessage));
+ big_buffer = test_malloc(REALLY_BIG);
+ iov.iov_base = big_buffer;
+ iov.iov_len = REALLY_BIG;
+ inmessage.msg_iov = &iov;
+ inmessage.msg_iovlen = 1;
+ inmessage.msg_control = incmsg;
+ inmessage.msg_controllen = sizeof(incmsg);
+ inmessage.msg_name = &msgname;
+ inmessage.msg_namelen = sizeof(msgname);
+ memset(&msgname, 0, sizeof(msgname));
+
+ /* Get the communication up message on sk. */
+ error = test_recvmsg(sk, &inmessage, MSG_WAITALL);
+ test_check_msg_notification(&inmessage, error,
+ sizeof(struct sctp_assoc_change),
+ SCTP_ASSOC_CHANGE, SCTP_COMM_UP);
+
+ printf("Established connection with ");
+ if (AF_INET == msgname.sa.sa_family)
+ printf("%d.%d.%d.%d(%d)\n", NIPQUAD(msgname.v4.sin_addr),
+ ntohs(msgname.v4.sin_port));
+ if (AF_INET6 == msgname.sa.sa_family)
+ printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x(%d)\n",
+ NIP6(msgname.v6.sin6_addr), ntohs(msgname.v6.sin6_port));
+
+ time(&from);
+ for (i=0; i<1000000; i++) {
+ inmessage.msg_controllen = sizeof(incmsg);
+ inmessage.msg_namelen = sizeof(msgname);
+ error = test_recvmsg(sk, &inmessage, MSG_WAITALL);
+ if (inmessage.msg_flags & MSG_NOTIFICATION)
+ break;
+ printf("Received %d bytes of data\n", error);
+ bytes_received += error;
+ }
+ time(&to);
+
+ printf("\t%d messages(%d bytes) successfully received in %ld "
+ "seconds.\n", i, bytes_received, to - from);
+ printf("The receive rate is %ld bytes/second\n",
+ bytes_received/(to - from));
+
+ /* Shut down the link. */
+ error = 0;
+ close(sk);
+
+ return 0;
+}
diff --git a/src/apps/nagle_snd.c b/src/apps/nagle_snd.c
new file mode 100644
index 0000000..bc960cc
--- /dev/null
+++ b/src/apps/nagle_snd.c
@@ -0,0 +1,359 @@
+/* SCTP kernel Implementation
+ * (C) Copyright IBM Corp. 2002, 2003
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ *
+ * The SCTP implementation 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.
+ *
+ * The SCTP implementation 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
+ *
+ * Any bugs reported to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ *
+ * Written or modified by:
+ * Ardelle Fan <ardelle.fan@intel.com>
+ * Sridhar Samudrala <sri@us.ibm.com>
+ */
+
+/* This is a receiver for the performance test to verify Nagle's algorithm.
+ * It creates a socket, binds to a address specified as a parameter and
+ * sends 1,000,000 packets to a specified target.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.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 <sctputil.h>
+#include <getopt.h>
+#include <netdb.h>
+
+char *TCID = __FILE__;
+int TST_TOTAL = 1;
+int TST_CNT = 0;
+
+void
+usage(char *argv0)
+{
+ fprintf(stderr, "Usage: %s -H localhost [-P localport] "
+ "-h remotehost [-p remoteport]\n"
+ "\t\t[-S msgsize] [-I interval] -N\n"
+ " -H, --local\t\tspecify one of the local addresses,\n"
+ " -P, --local-port\tspecify the port number for local addresses,\n"
+ " -h, --remote\t\tspecify one of the remote addresses,\n"
+ " -p, --remote-port\tspecify the port number for remote addresses,\n"
+ " -S, --size\t\tspecify the size(byte) of the sending message,\n"
+ " -I, --interval\t\tspecify the interval(second) that sending messages at,\n"
+ " -N, --nodelay\t\tspecify whether the SCTP allows Nagle's algorithm\n",
+ argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int sk, i;
+ struct addrinfo *hst_res, *tgt_res;
+ sockaddr_storage_t host, target;
+ sockaddr_storage_t msgname;
+ struct iovec iov;
+ struct msghdr inmessage;
+ struct msghdr outmessage;
+ char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))];
+ char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+ struct cmsghdr *cmsg;
+ struct sctp_sndrcvinfo *sinfo;
+ struct iovec out_iov;
+ char *message;
+ int error, pf_class;
+ sctp_assoc_t associd;
+ uint32_t ppid;
+ uint32_t stream;
+ char *remote_host = NULL;
+ int remote_port = SCTP_TESTPORT_1;
+ char *local_host = NULL;
+ int local_port = SCTP_TESTPORT_2;
+ int size = 1;
+ int interval = 0;
+ int nodelay = 0;
+ int option_index = 0;
+ char *big_buffer;
+ char port_buffer[10];
+ int c;
+ static struct option long_options[] = {
+ {"local", 1, 0, 1},
+ {"local-port", 1, 0, 2},
+ {"remote", 1, 0, 3},
+ {"remote-port", 1, 0, 4},
+ {"size", 1, 0, 5},
+ {"interval", 1, 0, 6},
+ {"nodelay", 0, 0, 10},
+ {0, 0, 0, 0}
+ };
+
+ /* Rather than fflush() throughout the code, set stdout to
+ * be unbuffered.
+ */
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /* Parse the arguments. */
+ while (1) {
+ c = getopt_long (argc, argv, "H:P:h:p:S:I:N",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ printf("option %s", long_options[option_index].name);
+ if (optarg) {
+ printf(" with arg %s", optarg);
+ }
+ printf("\n");
+ break;
+ case 1: /* local host */
+ case 'H':
+ local_host = optarg;
+ break;
+ case 2: /* local port */
+ case 'P':
+ local_port = atoi(optarg);
+ break;
+ case 3: /* remote host */
+ case 'h':
+ remote_host = optarg;
+ break;
+ case 4: /* remote port */
+ case 'p':
+ remote_port = atoi(optarg);
+ break;
+ case 5: /* size */
+ case 'S':
+ size = atoi(optarg);
+ break;
+ case 6: /* interval */
+ case 'I':
+ interval = atoi(optarg);
+ break;
+ case 10: /* nodelay */
+ case 'N':
+ nodelay = 1;
+ break;
+ case '?':
+ usage(argv[0]);
+ exit(0);
+
+ default:
+ printf ("%s: unrecognized option 0%c\n",
+ argv[0], c);
+ 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 (!local_host || !remote_host) {
+ fprintf(stderr, "%s: : option --local and --remote are required\n",
+ argv[0]);
+ usage(argv[0]);
+ exit(1);
+ }
+
+ /* Set some basic values which depend on the address family. */
+ if (!strcmp(local_host, "0"))
+ local_host = "0.0.0.0";
+
+ snprintf(port_buffer, 10, "%d", local_port);
+ error = getaddrinfo(local_host, port_buffer, NULL, &hst_res);
+ if (error) {
+ fprintf(stderr, "%s: getaddrinfo failed: %s\n", argv[0], local_host);
+ exit(1);
+ }
+
+ snprintf(port_buffer, 10, "%d", remote_port);
+ error = getaddrinfo(remote_host, port_buffer, NULL, &tgt_res);
+ if (error) {
+ fprintf(stderr, "%s: getaddrinfo failed: %s\n", argv[0], remote_host);
+ exit(1);
+ }
+
+ if ( hst_res->ai_family != tgt_res->ai_family) {
+ fprintf(stderr, "local and reomte hosts should be the "
+ "same address family\n");
+ exit(1);
+ }
+
+ pf_class = hst_res->ai_family;
+ switch (pf_class) {
+ case AF_INET:
+ case AF_INET6:
+ memcpy(&host.sa, hst_res->ai_addr, hst_res->ai_addrlen);
+ memcpy(&target.sa, tgt_res->ai_addr, tgt_res->ai_addrlen);
+ break;
+ default:
+ fprintf(stderr, "Invalid address type.\n");
+ exit(1);
+ break;
+ }
+
+ freeaddrinfo(hst_res);
+ freeaddrinfo(tgt_res);
+
+ sk = test_socket(pf_class, SOCK_SEQPACKET, IPPROTO_SCTP);
+
+ /* Enable ASSOC_CHANGE and SNDRCVINFO notifications. */
+ test_enable_assoc_change(sk);
+
+ test_setsockopt(sk, SCTP_NODELAY, &nodelay, sizeof(int));
+
+ /* Bind the sockets to the test port. */
+ test_bind(sk, &host.sa, sizeof(host));
+
+ /* Mark sk as being able to accept new associations. */
+ test_listen(sk, 1);
+
+ /* Build up a msghdr structure we can use for all sending. */
+ outmessage.msg_name = ⌖
+ outmessage.msg_namelen = sizeof(target);
+ outmessage.msg_iov = &out_iov;
+ outmessage.msg_iovlen = 1;
+ outmessage.msg_control = outcmsg;
+ outmessage.msg_controllen = sizeof(outcmsg);
+ outmessage.msg_flags = 0;
+ cmsg = CMSG_FIRSTHDR(&outmessage);
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+ outmessage.msg_controllen = cmsg->cmsg_len;
+ sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
+ ppid = rand(); /* Choose an arbitrary value. */
+ stream = 1;
+ sinfo->sinfo_ppid = ppid;
+ sinfo->sinfo_stream = stream;
+
+ message = test_malloc((size + 1) * sizeof(u_int8_t));
+
+ for(i=0; i + 10 < size; i+= 10)
+ memcpy(message+i, "1234567890", 10);
+ strncpy(message+i, "1234567890", size-i);
+ *(message+size) = 0;
+
+ outmessage.msg_iov->iov_base = message;
+ outmessage.msg_iov->iov_len = size + 1;
+
+ printf("Initiating connection with %s:%d...\n", remote_host,
+ remote_port);
+
+ /* Send the first message. This will create the association. */
+ test_sendmsg(sk, &outmessage, 0, size+1);
+
+ memset(&inmessage, 0, sizeof(inmessage));
+ big_buffer = test_malloc(REALLY_BIG);
+ iov.iov_base = big_buffer;
+ iov.iov_len = REALLY_BIG;
+ inmessage.msg_iov = &iov;
+ inmessage.msg_iovlen = 1;
+ inmessage.msg_control = incmsg;
+ inmessage.msg_controllen = sizeof(incmsg);
+ inmessage.msg_name = &msgname;
+ inmessage.msg_namelen = sizeof(msgname);
+ memset(&msgname, 0, sizeof(msgname));
+
+ /* Get the communication up message on sk. */
+ error = test_recvmsg(sk, &inmessage, MSG_WAITALL);
+ test_check_msg_notification(&inmessage, error,
+ sizeof(struct sctp_assoc_change),
+ SCTP_ASSOC_CHANGE, SCTP_COMM_UP);
+ associd = ((struct sctp_assoc_change *)iov.iov_base)->sac_assoc_id;
+
+ printf("Established connection with ");
+ if (AF_INET == msgname.sa.sa_family)
+ printf("%d.%d.%d.%d(%d)\n", NIPQUAD(msgname.v4.sin_addr),
+ ntohs(msgname.v4.sin_port));
+ if (AF_INET6 == msgname.sa.sa_family)
+ printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x(%d)\n",
+ NIP6(msgname.v6.sin6_addr), ntohs(msgname.v6.sin6_port));
+
+ printf("Sending data to receiver...\n");
+
+ for (i=1; i<1000000; i++) {
+
+ if (interval)
+ sleep(interval);
+
+ outmessage.msg_name = ⌖
+ outmessage.msg_namelen = sizeof(target);
+ outmessage.msg_iov = &out_iov;
+ outmessage.msg_iovlen = 1;
+ outmessage.msg_controllen = sizeof(outcmsg);
+ outmessage.msg_flags = 0;
+
+ cmsg = CMSG_FIRSTHDR(&outmessage);
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+ outmessage.msg_controllen = cmsg->cmsg_len;
+ sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
+
+ ppid++;
+
+ sinfo->sinfo_ppid = ppid;
+ sinfo->sinfo_stream = stream;
+ sinfo->sinfo_assoc_id = associd;
+
+ outmessage.msg_iov->iov_base = message;
+ outmessage.msg_iov->iov_len = size + 1;
+
+ test_sendmsg(sk, &outmessage, 0, size+1);
+ }
+
+ printf("\n\n\t\tComplete all the data sendings to receiver...\n\n\n");
+
+ error = 0;
+ close(sk);
+
+ free(message);
+
+ /* Indicate successful completion. */
+ return 0;
+
+}
diff --git a/src/apps/peel_client.c b/src/apps/peel_client.c
new file mode 100644
index 0000000..5c195d3
--- /dev/null
+++ b/src/apps/peel_client.c
@@ -0,0 +1,672 @@
+/* SCTP kernel Implementation
+ * (C) Copyright IBM Corp. 2003
+ *
+ * The SCTP implementation 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.
+ *
+ * The SCTP implementation 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.
+ *
+ * In addition:
+ *
+ * Copyright (c) 2003 Cisco
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * a) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * b) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * c) Neither the name of Cisco Systems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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
+ *
+ * Any bugs reported to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ *
+ * Written or modified by:
+ * Randall Stewart <rrs@cisco.com>
+ * Sridhar Samudrala <sri@us.ibm.com>
+ */
+
+#include <ctype.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <stdarg.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/uio.h>
+#include <netdb.h>
+
+#include <netinet/sctp.h>
+#ifdef __NetBSD__
+#include <sys/inttypes.h>
+#endif
+
+#define SCTP_CRC32C_POLY 0x1EDC6F41
+#define SCTP_CRC32C(c,d) (c=(c>>8)^sctp_crc_c[(c^(d))&0xFF])
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* Copyright 2001, D. Otis. Use this program, code or tables */
+/* extracted from it, as desired without restriction. */
+/* */
+/* 32 Bit Reflected CRC table generation for SCTP. */
+/* To accommodate serial byte data being shifted out least */
+/* significant bit first, the table's 32 bit words are reflected */
+/* which flips both byte and bit MS and LS positions. The CRC */
+/* is calculated MS bits first from the perspective of the serial*/
+/* stream. The x^32 term is implied and the x^0 term may also */
+/* be shown as +1. The polynomial code used is 0x1EDC6F41. */
+/* Castagnoli93 */
+/* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+ */
+/* x^11+x^10+x^9+x^8+x^6+x^0 */
+/* Guy Castagnoli Stefan Braeuer and Martin Herrman */
+/* "Optimization of Cyclic Redundancy-Check Codes */
+/* with 24 and 32 Parity Bits", */
+/* IEEE Transactions on Communications, Vol.41, No.6, June 1993 */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static unsigned char buffer1[4100];
+static unsigned char buffer2[4100];
+static unsigned char buffer3[4100];
+static unsigned char buffer4[4100];
+static struct sockaddr_in bindto,got,to;
+static socklen_t len;
+
+unsigned long sctp_crc_c[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+ 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+ 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+ 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+ 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+ 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+ 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+ 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+ 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+ 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+ 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+ 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+ 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+ 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+ 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+ 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+ 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+ 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+ 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+ 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+ 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+ 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+ 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+ 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+ 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+ 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+ 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+ 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+ 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+ 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+ 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+ 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+ 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+ 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+ 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+ 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+ 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+ 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+ 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+ 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+ 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+ 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+ 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+ 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+ 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+ 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+ 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+ 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+ 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+ 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+ 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+ 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+ 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+ 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+ 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+ 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+ 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L,
+};
+
+u_int32_t
+update_crc32(u_int32_t crc32,
+ unsigned char *buffer,
+ unsigned int length)
+{
+ int i;
+ for (i = 0; i < length; i++) {
+ SCTP_CRC32C(crc32, buffer[i]);
+ }
+ return (crc32);
+}
+
+u_int32_t
+sctp_csum_finalize(u_int32_t crc32)
+{
+ u_int32_t result;
+#if BYTE_ORDER == BIG_ENDIAN
+ u_int8_t byte0, byte1, byte2, byte3;
+#endif
+ /* Complement the result */
+ result = ~crc32;
+#if BYTE_ORDER == BIG_ENDIAN
+ /*
+ * For BIG-ENDIAN.. aka Motorola byte order the result is in
+ * little-endian form. So we must manually swap the bytes. Then
+ * we can call htonl() which does nothing...
+ */
+ byte0 = result & 0x000000ff;
+ byte1 = (result >> 8) & 0x000000ff;
+ byte2 = (result >> 16) & 0x000000ff;
+ byte3 = (result >> 24) & 0x000000ff;
+ result = ((byte0 << 24) |
+ (byte1 << 16) |
+ (byte2 << 8) |
+ byte3);
+ crc32 = htonl(result);
+#else
+ /*
+ * For INTEL platforms the result comes out in network order.
+ * No htonl is required or the swap above. So we optimize out
+ * both the htonl and the manual swap above.
+ */
+ crc32 = result;
+#endif
+ return (crc32);
+}
+
+static u_int32_t at = 468700;
+
+void
+prepare_buffers(void)
+{
+ /* Prepare buffers */
+ u_int32_t temp,*p;
+ int i;
+
+ p = (u_int32_t *)buffer1;
+ for(i=0;i<(sizeof(buffer1)/4);i++){
+ *p = at;
+ at++;
+ p++;
+ }
+ /* backup over the last int */
+ p--;
+ *p = 0;
+ temp = 0xffffffff;
+ temp = update_crc32(temp,buffer1,(sizeof(buffer1)-4));
+ *p = sctp_csum_finalize(temp);
+
+ p = (u_int32_t *)buffer2;
+ for(i=0;i<(sizeof(buffer2)/4);i++){
+ *p = at;
+ at++;
+ p++;
+ }
+ p--;
+ *p = 0;
+ temp = 0xffffffff;
+ temp = update_crc32(temp,buffer2,(sizeof(buffer2)-4));
+ *p = sctp_csum_finalize(temp);
+
+ p = (u_int32_t *)buffer3;
+ for(i=0;i<(sizeof(buffer3)/4);i++){
+ *p = at;
+ at++;
+ p++;
+ }
+ p--;
+ *p = 0;
+ temp = 0xffffffff;
+ temp = update_crc32(temp,buffer3,(sizeof(buffer3)-4));
+ *p = sctp_csum_finalize(temp);
+
+ p = (u_int32_t *)buffer4;
+ for(i=0;i<(sizeof(buffer4)/4);i++){
+ *p = at;
+ at++;
+ p++;
+ }
+ p--;
+ *p = 0;
+ temp = 0xffffffff;
+ temp = update_crc32(temp,buffer4,(sizeof(buffer4)-4));
+ *p = sctp_csum_finalize(temp);
+}
+
+static int
+my_handle_notification(int fd,char *notify_buf) {
+ union sctp_notification *snp;
+ struct sctp_assoc_change *sac;
+ struct sctp_paddr_change *spc;
+ struct sctp_remote_error *sre;
+ struct sctp_send_failed *ssf;
+ struct sctp_shutdown_event *sse;
+ int asocDown;
+ char *str;
+ char buf[256];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ asocDown = 0;
+ snp = (union sctp_notification *)notify_buf;
+ switch(snp->sn_header.sn_type) {
+ case SCTP_ASSOC_CHANGE:
+ sac = &snp->sn_assoc_change;
+ switch(sac->sac_state) {
+
+ case SCTP_COMM_UP:
+ str = "COMMUNICATION UP";
+ break;
+ case SCTP_COMM_LOST:
+ str = "COMMUNICATION LOST";
+ asocDown = 1;
+ break;
+ case SCTP_RESTART:
+ str = "RESTART";
+ break;
+ case SCTP_SHUTDOWN_COMP:
+ str = "SHUTDOWN COMPLETE";
+ asocDown = 1;
+ break;
+ case SCTP_CANT_STR_ASSOC:
+ str = "CANT START ASSOC";
+ printf("EXIT:SCTP_ASSOC_CHANGE: %s, assoc=%xh\n", str,
+ (uint32_t)sac->sac_assoc_id);
+ exit(0);
+ break;
+ default:
+ str = "UNKNOWN";
+ } /* end switch(sac->sac_state) */
+ printf("SCTP_ASSOC_CHANGE: %s, assoc=%xh\n", str,
+ (uint32_t)sac->sac_assoc_id);
+ break;
+ case SCTP_PEER_ADDR_CHANGE:
+ spc = &snp->sn_paddr_change;
+ switch(spc->spc_state) {
+ case SCTP_ADDR_AVAILABLE:
+ str = "ADDRESS AVAILABLE";
+ break;
+ case SCTP_ADDR_UNREACHABLE:
+ str = "ADDRESS UNAVAILABLE";
+ break;
+ case SCTP_ADDR_REMOVED:
+ str = "ADDRESS REMOVED";
+ break;
+ case SCTP_ADDR_ADDED:
+ str = "ADDRESS ADDED";
+ break;
+ case SCTP_ADDR_MADE_PRIM:
+ str = "ADDRESS MADE PRIMARY";
+ break;
+ default:
+ str = "UNKNOWN";
+ } /* end switch */
+ sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;
+ if (sin6->sin6_family == AF_INET6) {
+ inet_ntop(AF_INET6, (char*)&sin6->sin6_addr, buf, sizeof(buf));
+ } else {
+ sin = (struct sockaddr_in *)&spc->spc_aaddr;
+ inet_ntop(AF_INET, (char*)&sin->sin_addr, buf, sizeof(buf));
+ }
+ printf("SCTP_PEER_ADDR_CHANGE: %s, addr=%s, assoc=%xh\n", str,
+ buf, (uint32_t)spc->spc_assoc_id);
+ break;
+ case SCTP_REMOTE_ERROR:
+ sre = &snp->sn_remote_error;
+ printf("SCTP_REMOTE_ERROR: assoc=%xh\n",
+ (uint32_t)sre->sre_assoc_id);
+ break;
+ case SCTP_SEND_FAILED:
+ ssf = &snp->sn_send_failed;
+ printf("SCTP_SEND_FAILED: assoc=%xh\n",
+ (uint32_t)ssf->ssf_assoc_id);
+ break;
+ case SCTP_ADAPTATION_INDICATION:
+ {
+ struct sctp_adaptation_event *ae;
+ ae = &snp->sn_adaptation_event;
+ printf("\nSCTP_adaptation_indication bits:0x%x\n",
+ (u_int)ae->sai_adaptation_ind);
+ }
+ break;
+ case SCTP_PARTIAL_DELIVERY_EVENT:
+ {
+ struct sctp_pdapi_event *pdapi;
+ pdapi = &snp->sn_pdapi_event;
+ printf("SCTP_PD-API event:%u\n",
+ pdapi->pdapi_indication);
+ if(pdapi->pdapi_indication == 0){
+ printf("PDI- Aborted\n");
+ }
+ }
+ break;
+
+ case SCTP_SHUTDOWN_EVENT:
+ sse = &snp->sn_shutdown_event;
+ printf("SCTP_SHUTDOWN_EVENT: assoc=%xh\n",
+ (uint32_t)sse->sse_assoc_id);
+ break;
+ default:
+ printf("Unknown notification event type=%xh\n",
+ snp->sn_header.sn_type);
+ } /* end switch(snp->sn_type) */
+ if(asocDown){
+ printf("Bring association back up\n");
+ len = sizeof(to);
+ if(connect(fd,(struct sockaddr *)&to,len) == -1){
+ printf("Sorry connect fails %d\n",errno);
+ }
+ return(1);
+ }
+ return(0);
+}
+
+
+
+int
+my_sctpReadInput(int fd)
+{
+ /* receive some number of datagrams and
+ * act on them.
+ */
+ int sz;
+ struct msghdr msg;
+ struct iovec iov[2];
+ unsigned char from[200];
+ char readBuffer[65535];
+ char controlVector[65535];
+
+ iov[0].iov_base = readBuffer;
+ iov[0].iov_len = sizeof(readBuffer);
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ msg.msg_name = (caddr_t)from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = (caddr_t)controlVector;
+ msg.msg_controllen = sizeof(controlVector);
+ msg.msg_flags = 0;
+ errno = 0;
+ sz = recvmsg(fd,&msg,0);
+ if(sz <= 0){
+ printf("Read returns %d errno:%d control len is %zu msgflg:%x\n",
+ sz,errno,
+ msg.msg_controllen,msg.msg_flags);
+ }
+ if (msg.msg_flags & MSG_NOTIFICATION) {
+ return(my_handle_notification(fd,readBuffer));
+ }else{
+ printf("Huh, I got data?.. ignored (%d bytes)\n",sz);
+ return(0);
+ }
+}
+
+
+int
+clear_fds(int fd,int fd1)
+{
+ int felldown;
+ int max,notdone;
+ fd_set readfds,writefds,exceptfds;
+ struct timeval tv;
+ memset(&tv,0,sizeof(tv));
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ felldown = 0;
+ FD_SET(fd,&readfds);
+ FD_SET(fd1,&readfds);
+ if(fd > fd1){
+ max = fd + 1;
+ }else{
+ max = fd1 + 1;
+ }
+ notdone = 1;
+ while(notdone){
+ if (select(max, &readfds, &writefds, &exceptfds, &tv) == -1)
+ break;
+ notdone = 0;
+ if(FD_ISSET(fd,&readfds)){
+ notdone++;
+ printf("clearing fd:%d\n",fd);
+ felldown += my_sctpReadInput(fd);
+ notdone = 1;
+ }
+ if(FD_ISSET(fd1,&readfds)){
+ notdone++;
+ printf("clearing fd1:%d\n",fd1);
+ felldown += my_sctpReadInput(fd1);
+ }
+ }
+ return(felldown);
+}
+
+void
+process_out_data(int fd,int fd1)
+{
+ int notdone,x,ret;
+ while(1){
+ /* Prepare the buffers */
+ prepare_buffers();
+ /* send out the 4 buffers */
+ ret = sendto(fd,buffer1,sizeof(buffer1),0,
+ (struct sockaddr *)&to,sizeof(to));
+ if(ret < sizeof(buffer1)){
+ printf("Gak1, error:%d ret:%d\n",errno,ret);
+ }
+
+ ret = sendto(fd1,buffer2,sizeof(buffer1),0,
+ (struct sockaddr *)&to,sizeof(to));
+ if(ret < sizeof(buffer2)){
+ printf("Gak2, error:%d ret:%d\n",errno,ret);
+ }
+
+
+ ret = sendto(fd,buffer3,sizeof(buffer1),0,
+ (struct sockaddr *)&to,sizeof(to));
+ if(ret < sizeof(buffer3)){
+ printf("Gak3, error:%d ret:%d\n",errno,ret);
+ }
+
+
+ ret = sendto(fd1,buffer4,sizeof(buffer1),0,
+ (struct sockaddr *)&to,sizeof(to));
+ if(ret < sizeof(buffer4)){
+ printf("Gak4, error:%d ret:%d\n",errno,ret);
+ }
+
+ /* now wait until we get a assoc failing */
+ notdone = 1;
+ while(notdone){
+ x = clear_fds(fd,fd1);
+ if(x){
+ notdone = 0;
+ }
+ sleep(1);
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int i,fd,fd1;
+ char *addr=NULL;
+ uint16_t port=0;
+ int protocol_touse = IPPROTO_SCTP;
+ struct sctp_event_subscribe event = {0};
+
+ while((i= getopt(argc,argv,"p:h:")) != EOF){
+ switch(i){
+ case 'h':
+ addr = optarg;
+ break;
+ case 'p':
+ port = (uint16_t)strtol(optarg,NULL,0);
+ break;
+ };
+ }
+ memset(&to,0,sizeof(to));
+ if((addr == NULL) || (port == 0)){
+ printf("Usage: %s -h addr -p port\n", argv[0]);
+ return(-1);
+ }
+ if(inet_pton(AF_INET, addr, (void *) &to.sin_addr)){
+ //to.sin_len = sizeof(to);
+ to.sin_family = AF_INET;
+ printf("port selected is %d\n",port);
+ printf("addr %x\n",(u_int)ntohl(to.sin_addr.s_addr));
+ to.sin_port = htons(port);
+ }else{
+ printf("Can't translate the address\n");
+ return(-1);
+ }
+ /**********************socket 1 *******************/
+ fd = socket(AF_INET, SOCK_SEQPACKET, protocol_touse);
+ if(fd == -1){
+ printf("can't open socket:%d\n",errno);
+ return(-1);
+ }
+ memset(&bindto,0,sizeof(bindto));
+ //len = bindto.sin_len = sizeof(bindto);
+ len = sizeof(bindto);
+ bindto.sin_family = AF_INET;
+ bindto.sin_port = 0;
+ if(bind(fd,(struct sockaddr *)&bindto, len) < 0){
+ printf("can't bind a socket:%d\n",errno);
+ close(fd);
+ return(-1);
+ }
+ if(getsockname(fd,(struct sockaddr *)&got,&len) < 0){
+ printf("get sockname failed err:%d\n",errno);
+ close(fd);
+ return(-1);
+ }
+ printf("fd uses port %d\n",ntohs(got.sin_port));
+
+ /**********************socket 2 *******************/
+ fd1 = socket(AF_INET, SOCK_SEQPACKET, protocol_touse);
+ if(fd1 == -1){
+ printf("can't open socket:%d\n",errno);
+ close(fd);
+ return(-1);
+ }
+ memset(&bindto,0,sizeof(bindto));
+ //len = bindto.sin_len = sizeof(bindto);
+ len = sizeof(bindto);
+ bindto.sin_family = AF_INET;
+ bindto.sin_port = 0;
+ if(bind(fd1,(struct sockaddr *)&bindto, len) < 0){
+ printf("can't bind a socket:%d\n",errno);
+ close(fd);
+ close(fd1);
+ return(-1);
+ }
+ if(getsockname(fd1,(struct sockaddr *)&got,&len) < 0){
+ printf("get sockname failed err:%d\n",errno);
+ close(fd);
+ close(fd1);
+ return(-1);
+ }
+ printf("fd1 uses port %d\n",ntohs(got.sin_port));
+
+ /* enable all event notifications */
+ event.sctp_data_io_event = 1;
+ event.sctp_association_event = 1;
+ event.sctp_address_event = 1;
+ event.sctp_send_failure_event = 1;
+ event.sctp_peer_error_event = 1;
+ event.sctp_shutdown_event = 1;
+ event.sctp_partial_delivery_event = 1;
+ event.sctp_adaptation_layer_event = 1;
+ if (setsockopt(fd, IPPROTO_SCTP,
+ SCTP_EVENTS, &event,
+ sizeof(event)) != 0) {
+ printf("Gak, can't set events errno:%d\n",errno);
+ exit(0);
+ }
+ if (setsockopt(fd1, IPPROTO_SCTP,
+ SCTP_EVENTS, &event,
+ sizeof(event)) != 0) {
+ printf("Gak, can't set events errno:%d\n",errno);
+ exit(0);
+ }
+
+ if(connect(fd,(struct sockaddr *)&to,len) == -1){
+ printf("Sorry connect fails %d\n",errno);
+ close(fd);
+ return(-1);
+ }
+ if(connect(fd1,(struct sockaddr *)&to,len) == -1){
+ printf("Sorry connect fails %d\n",errno);
+ close(fd);
+ return(-1);
+ }
+ printf("Connected\n");
+ clear_fds(fd,fd1);
+ process_out_data(fd,fd1);
+
+ return(0);
+}
+
diff --git a/src/apps/peel_server.c b/src/apps/peel_server.c
new file mode 100644
index 0000000..24a1ef8
--- /dev/null
+++ b/src/apps/peel_server.c
@@ -0,0 +1,667 @@
+/* SCTP kernel Implementation
+ * (C) Copyright IBM Corp. 2003
+ * Copyright (c) 2003 Cisco
+ *
+ * The SCTP implementation 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.
+ *
+ * The SCTP implementation 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.
+ *
+ * In addition:
+ *
+ * Copyright (c) 2003 Cisco
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * a) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * b) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * c) Neither the name of Cisco Systems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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
+ *
+ * Any bugs reported to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ *
+ * Written or modified by:
+ * Randall Stewart <rrs@cisco.com>
+ * Sridhar Samudrala <sri@us.ibm.com>
+ */
+
+#include <ctype.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <stdarg.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/uio.h>
+#include <netdb.h>
+
+#include <netinet/sctp.h>
+#ifdef __NetBSD__
+#include <sys/inttypes.h>
+#endif
+
+#define SCTP_CRC32C_POLY 0x1EDC6F41
+#define SCTP_CRC32C(c,d) (c=(c>>8)^sctp_crc_c[(c^(d))&0xFF])
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* Copyright 2001, D. Otis. Use this program, code or tables */
+/* extracted from it, as desired without restriction. */
+/* */
+/* 32 Bit Reflected CRC table generation for SCTP. */
+/* To accommodate serial byte data being shifted out least */
+/* significant bit first, the table's 32 bit words are reflected */
+/* which flips both byte and bit MS and LS positions. The CRC */
+/* is calculated MS bits first from the perspective of the serial*/
+/* stream. The x^32 term is implied and the x^0 term may also */
+/* be shown as +1. The polynomial code used is 0x1EDC6F41. */
+/* Castagnoli93 */
+/* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+ */
+/* x^11+x^10+x^9+x^8+x^6+x^0 */
+/* Guy Castagnoli Stefan Braeuer and Martin Herrman */
+/* "Optimization of Cyclic Redundancy-Check Codes */
+/* with 24 and 32 Parity Bits", */
+/* IEEE Transactions on Communications, Vol.41, No.6, June 1993 */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static unsigned char buffer1[4100];
+static unsigned char buffer2[4100];
+static unsigned char buffer3[4100];
+static unsigned char buffer4[4100];
+static struct sockaddr_in bindto,got;
+static socklen_t len;
+
+unsigned long sctp_crc_c[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+ 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+ 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+ 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+ 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+ 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+ 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+ 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+ 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+ 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+ 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+ 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+ 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+ 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+ 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+ 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+ 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+ 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+ 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+ 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+ 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+ 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+ 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+ 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+ 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+ 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+ 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+ 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+ 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+ 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+ 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+ 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+ 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+ 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+ 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+ 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+ 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+ 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+ 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+ 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+ 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+ 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+ 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+ 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+ 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+ 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+ 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+ 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+ 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+ 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+ 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+ 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+ 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+ 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+ 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+ 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+ 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L,
+};
+
+u_int32_t
+update_crc32(u_int32_t crc32,
+ unsigned char *buffer,
+ unsigned int length)
+{
+ int i;
+ for (i = 0; i < length; i++) {
+ SCTP_CRC32C(crc32, buffer[i]);
+ }
+ return (crc32);
+}
+
+u_int32_t
+sctp_csum_finalize(u_int32_t crc32)
+{
+ u_int32_t result;
+#if BYTE_ORDER == BIG_ENDIAN
+ u_int8_t byte0, byte1, byte2, byte3;
+#endif
+ /* Complement the result */
+ result = ~crc32;
+#if BYTE_ORDER == BIG_ENDIAN
+ /*
+ * For BIG-ENDIAN.. aka Motorola byte order the result is in
+ * little-endian form. So we must manually swap the bytes. Then
+ * we can call htonl() which does nothing...
+ */
+ byte0 = result & 0x000000ff;
+ byte1 = (result >> 8) & 0x000000ff;
+ byte2 = (result >> 16) & 0x000000ff;
+ byte3 = (result >> 24) & 0x000000ff;
+ result = ((byte0 << 24) |
+ (byte1 << 16) |
+ (byte2 << 8) |
+ byte3);
+ crc32 = htonl(result);
+#else
+ /*
+ * For INTEL platforms the result comes out in network order.
+ * No htonl is required or the swap above. So we optimize out
+ * both the htonl and the manual swap above.
+ */
+ crc32 = result;
+#endif
+ return (crc32);
+}
+
+int
+check_buffers(void)
+{
+ /* Prepare buffers */
+ u_int32_t temp,*csum;
+ int ret=0;
+ temp = 0xffffffff;
+ temp = update_crc32(temp,buffer1,(sizeof(buffer1)-4));
+ temp = sctp_csum_finalize(temp);
+ csum = (u_int32_t *)(buffer1+(sizeof(buffer1)-4));
+ if(*csum != temp){
+ printf("Buffer1: Found csum:%x calculated:%x\n",
+ *csum,temp);
+ ret++;
+ }
+ temp = 0xffffffff;
+ temp = update_crc32(temp,buffer2,(sizeof(buffer2)-4));
+ temp = sctp_csum_finalize(temp);
+ csum = (u_int32_t *)(buffer2+(sizeof(buffer2)-4));
+ if(*csum != temp){
+ printf("Buffer2: Found csum:%x calculated:%x\n",
+ *csum,temp);
+ ret++;
+ }
+
+ temp = 0xffffffff;
+ temp = update_crc32(temp,buffer3,(sizeof(buffer3)-4));
+ temp = sctp_csum_finalize(temp);
+ csum = (u_int32_t *)(buffer3+(sizeof(buffer3)-4));
+ if(*csum != temp){
+ printf("Buffer3: Found csum:%x calculated:%x\n",
+ *csum,temp);
+ ret++;
+ }
+
+ temp = 0xffffffff;
+ temp = update_crc32(temp,buffer4,(sizeof(buffer4)-4));
+ temp = sctp_csum_finalize(temp);
+ csum = (u_int32_t *)(buffer4+(sizeof(buffer4)-4));
+ if(*csum != temp){
+ printf("Buffer4: Found csum:%x calculated:%x\n",
+ *csum,temp);
+ ret++;
+ }
+ return(ret);
+}
+
+static int
+my_handle_notification(int fd,char *notify_buf) {
+ union sctp_notification *snp;
+ struct sctp_assoc_change *sac;
+ struct sctp_paddr_change *spc;
+ struct sctp_remote_error *sre;
+ struct sctp_send_failed *ssf;
+ struct sctp_shutdown_event *sse;
+ int asocUp;
+ char *str;
+ char buf[256];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ snp = (union sctp_notification *)notify_buf;
+ asocUp = 0;
+ switch(snp->sn_header.sn_type) {
+ case SCTP_ASSOC_CHANGE:
+ sac = &snp->sn_assoc_change;
+ switch(sac->sac_state) {
+
+ case SCTP_COMM_UP:
+ str = "COMMUNICATION UP";
+ asocUp++;
+ break;
+ case SCTP_COMM_LOST:
+ str = "COMMUNICATION LOST";
+ break;
+ case SCTP_RESTART:
+ str = "RESTART";
+ asocUp++;
+ break;
+ case SCTP_SHUTDOWN_COMP:
+ str = "SHUTDOWN COMPLETE";
+ break;
+ case SCTP_CANT_STR_ASSOC:
+ str = "CANT START ASSOC";
+ printf("EXIT:SCTP_ASSOC_CHANGE: %s, assoc=%xh\n", str,
+ (uint32_t)sac->sac_assoc_id);
+ exit(0);
+ break;
+ default:
+ str = "UNKNOWN";
+ } /* end switch(sac->sac_state) */
+ printf("SCTP_ASSOC_CHANGE: %s, assoc=%xh\n", str,
+ (uint32_t)sac->sac_assoc_id);
+ break;
+ case SCTP_PEER_ADDR_CHANGE:
+ spc = &snp->sn_paddr_change;
+ switch(spc->spc_state) {
+ case SCTP_ADDR_AVAILABLE:
+ str = "ADDRESS AVAILABLE";
+ break;
+ case SCTP_ADDR_UNREACHABLE:
+ str = "ADDRESS UNAVAILABLE";
+ break;
+ case SCTP_ADDR_REMOVED:
+ str = "ADDRESS REMOVED";
+ break;
+ case SCTP_ADDR_ADDED:
+ str = "ADDRESS ADDED";
+ break;
+ case SCTP_ADDR_MADE_PRIM:
+ str = "ADDRESS MADE PRIMARY";
+ break;
+ default:
+ str = "UNKNOWN";
+ } /* end switch */
+ sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;
+ if (sin6->sin6_family == AF_INET6) {
+ inet_ntop(AF_INET6, (char*)&sin6->sin6_addr, buf, sizeof(buf));
+ } else {
+ sin = (struct sockaddr_in *)&spc->spc_aaddr;
+ inet_ntop(AF_INET, (char*)&sin->sin_addr, buf, sizeof(buf));
+ }
+ printf("SCTP_PEER_ADDR_CHANGE: %s, addr=%s, assoc=%xh\n", str,
+ buf, (uint32_t)spc->spc_assoc_id);
+ break;
+ case SCTP_REMOTE_ERROR:
+ sre = &snp->sn_remote_error;
+ printf("SCTP_REMOTE_ERROR: assoc=%xh\n",
+ (uint32_t)sre->sre_assoc_id);
+ break;
+ case SCTP_SEND_FAILED:
+ ssf = &snp->sn_send_failed;
+ printf("SCTP_SEND_FAILED: assoc=%xh\n",
+ (uint32_t)ssf->ssf_assoc_id);
+ break;
+ case SCTP_ADAPTATION_INDICATION:
+ {
+ struct sctp_adaptation_event *ae;
+ ae = &snp->sn_adaptation_event;
+ printf("\nSCTP_adaptation_indication bits:0x%x\n",
+ (u_int)ae->sai_adaptation_ind);
+ }
+ break;
+ case SCTP_PARTIAL_DELIVERY_EVENT:
+ {
+ struct sctp_pdapi_event *pdapi;
+ pdapi = &snp->sn_pdapi_event;
+ printf("SCTP_PD-API event:%u\n",
+ pdapi->pdapi_indication);
+ if(pdapi->pdapi_indication == 0){
+ printf("PDI- Aborted\n");
+ }
+ }
+ break;
+
+ case SCTP_SHUTDOWN_EVENT:
+ sse = &snp->sn_shutdown_event;
+ printf("SCTP_SHUTDOWN_EVENT: assoc=%xh\n",
+ (uint32_t)sse->sse_assoc_id);
+ break;
+ default:
+ printf("Unknown notification event type=%xh\n",
+ snp->sn_header.sn_type);
+ } /* end switch(snp->sn_type) */
+ return(asocUp);
+}
+
+
+static char readBuffer[65535];
+static int sz=0;
+static char controlVector[65535];
+static struct msghdr msg;
+int
+my_sctpReadInput(int fd,int maxread)
+{
+ /* receive some number of datagrams and
+ * act on them.
+ */
+ struct iovec iov[2];
+ unsigned char from[200];
+
+ memset(&msg,0,sizeof(msg));
+ memset(controlVector,0,sizeof(controlVector));
+ memset(readBuffer,0,sizeof(readBuffer));
+
+ iov[0].iov_base = readBuffer;
+ iov[0].iov_len = maxread;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ msg.msg_name = (caddr_t)from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = (caddr_t)controlVector;
+ msg.msg_controllen = sizeof(controlVector);
+ errno = 0;
+ sz = recvmsg(fd,&msg,0);
+ printf("Read fd:%d returns %d errno:%d control len is %zu msgflg:%x\n",
+ fd,
+ sz,errno,
+ msg.msg_controllen,
+ msg.msg_flags);
+
+ if (msg.msg_flags & MSG_NOTIFICATION) {
+ printf("Got a notification\n");
+ return(my_handle_notification(fd,readBuffer));
+ }else{
+ printf("Got data\n");
+ return(-1);
+ }
+}
+
+
+int
+poll_fd(int fd)
+{
+ int cameup;
+ int max;
+ fd_set readfds,writefds,exceptfds;
+ struct timeval tv;
+
+ memset(&tv,0,sizeof(tv));
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ cameup = 0;
+ max = fd + 1;
+
+ printf("poll_fd\n");
+ FD_SET(fd,&readfds);
+
+ if (select(max, &readfds, &writefds, &exceptfds, NULL) == -1)
+ return(cameup);
+ if(FD_ISSET(fd,&readfds)){
+ printf("Read please\n");
+ cameup += my_sctpReadInput(fd,4100);
+ }
+
+ return(cameup);
+}
+
+static sctp_assoc_t
+dig_out_asocid(void)
+{
+ struct sctp_sndrcvinfo *s_info;
+ struct cmsghdr *cmsg;
+ s_info = NULL;
+ if(msg.msg_controllen){
+ /* parse through and see if we find
+ * the sctp_sndrcvinfo
+ */
+ cmsg = (struct cmsghdr *)controlVector;
+ while(cmsg){
+ if(cmsg->cmsg_level == IPPROTO_SCTP){
+ if(cmsg->cmsg_type == SCTP_SNDRCV){
+ /* Got it */
+ s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ break;
+ }
+ }
+ cmsg = CMSG_NXTHDR(&msg,cmsg);
+ }
+ }else{
+ printf("No CMSG?\n");
+ exit(0);
+ }
+ if(s_info == NULL){
+ printf("No sinfo?\n");
+ exit(0);
+ }
+ return(s_info->sinfo_assoc_id);
+}
+
+
+void
+process(int fd,int magic)
+{
+ int fd1,num_asoc,ret,i;
+ sctp_assoc_t asoc;
+ struct timespec ts;
+ num_asoc = 0;
+ i = 1;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 10000;
+
+ while(i < 4099){
+ printf("pass %d\n",i);
+ while(num_asoc < 2){
+ ret = poll_fd(fd);
+ if(ret >0 ){
+ num_asoc += ret;
+ }else if(ret == 0){
+ sleep(1);
+ }else if(ret < 0){
+ printf("Got data? %d\n",sz);
+ sleep(1);
+ }
+ printf("asoc count is %d\n",num_asoc);
+ }
+ again:
+ printf("Reading for %d bytes from fd:%d\n",
+ i,fd);
+ my_sctpReadInput(fd,i);
+ if(sz == i){
+ memcpy(buffer1,readBuffer,i);
+ }else{
+ printf("Huh I am messed up read %d wanted %d\n",
+ sz,i);
+ goto again;
+ }
+ if(msg.msg_flags & MSG_EOR){
+ printf("Huh got EOR on paritial read?\n");
+ exit(0);
+ }
+ asoc = dig_out_asocid();
+ nanosleep(&ts,NULL);
+
+ fd1 = sctp_peeloff(fd,asoc);
+ if(fd1 == -1){
+ printf("peeloff failed %d/err:%d\n",
+ fd1,errno);
+ exit(0);
+ }
+ my_sctpReadInput(fd1,(4100-i));
+ if(sz > 0){
+ memcpy(&buffer1[i],readBuffer,sz);
+ printf("Copied %d bytes\n",sz);
+ }else{
+ printf("Huh only read %d\n",sz);
+ }
+ if(magic >= i){
+ printf("magic engaged\n");
+ my_sctpReadInput(fd,i);
+ }else{
+ my_sctpReadInput(fd,4100);
+ }
+ if(sz > 0){
+ memcpy(buffer2,readBuffer,sz);
+ printf("copied %d bytes\n",sz);
+ }else{
+ printf("Huh only read %d\n",sz);
+ }
+ my_sctpReadInput(fd1,4100);
+ if(sz > 0){
+ memcpy(buffer3,readBuffer,sz);
+ printf("copied %d bytes\n",sz);
+ }else{
+ printf("Huh only read %d\n",sz);
+ }
+ my_sctpReadInput(fd,4100);
+ if(sz > 0){
+ memcpy(buffer4,readBuffer,sz);
+ printf("copied %d bytes\n",sz);
+ }else{
+ printf("Huh only read %d\n",sz);
+ }
+ if(check_buffers()){
+ exit(0);
+ }
+ close(fd1);
+ i++;
+ num_asoc--;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int i,fd;
+ uint16_t myport=0;
+ int magic=0;
+ struct sctp_event_subscribe event = {0};
+ while((i= getopt(argc,argv,"m:M:")) != EOF){
+ switch(i){
+ case 'm':
+ myport = (uint16_t)strtol(optarg,NULL,0);
+ break;
+ case 'M':
+ magic = strtol(optarg,NULL,0);
+ break;
+ };
+ }
+ /**********************socket 1 *******************/
+ fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
+ if(fd == -1){
+ printf("can't open socket:%d\n",errno);
+ return(-1);
+ }
+ memset(&bindto,0,sizeof(bindto));
+ //len = bindto.sin_len = sizeof(bindto);
+ len = sizeof(bindto);
+ bindto.sin_family = AF_INET;
+ printf("bind port %d\n",myport);
+ bindto.sin_port = htons(myport);
+ if(bind(fd,(struct sockaddr *)&bindto, len) < 0){
+ printf("can't bind a socket:%d\n",errno);
+ close(fd);
+ return(-1);
+ }
+ if(getsockname(fd,(struct sockaddr *)&got,&len) < 0){
+ printf("get sockname failed err:%d\n",errno);
+ close(fd);
+ return(-1);
+ }
+ printf("fd uses port %d\n",ntohs(got.sin_port));
+ if (listen(fd, 100) == -1) {
+ printf("listen err: %d\n", errno);
+ close(fd);
+ return(-1);
+ }
+ /* enable all event notifications */
+ event.sctp_data_io_event = 1;
+ event.sctp_association_event = 1;
+ event.sctp_address_event = 1;
+ event.sctp_send_failure_event = 1;
+ event.sctp_peer_error_event = 1;
+ event.sctp_shutdown_event = 1;
+ event.sctp_partial_delivery_event = 1;
+ event.sctp_adaptation_layer_event = 1;
+ if (setsockopt(fd, IPPROTO_SCTP,
+ SCTP_EVENTS, &event,
+ sizeof(event)) != 0) {
+ printf("Gak, can't set events errno:%d\n",errno);
+ exit(0);
+ }
+ printf("to process\n");
+ process(fd,magic);
+ return(0);
+}
+
diff --git a/src/apps/sctp_darn.c b/src/apps/sctp_darn.c
new file mode 100644
index 0000000..bcfb822
--- /dev/null
+++ b/src/apps/sctp_darn.c
@@ -0,0 +1,2414 @@
+/* SCTP kernel Implementation
+ * (C) Copyright IBM Corp. 2001, 2003
+ * Copyright (c) 1999 Cisco
+ * Copyright (c) 1999, 2000, 2001 Motorola
+ * Copyright (c) 2001 Nokia
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * The SCTP implementation 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.
+ *
+ * The SCTP implementation 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
+ *
+ * Any bugs reported to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ *
+ * Written or modified by:
+ * La Monte H.P. Yarroll <piggy@acm.org>
+ * Karl Knutson <karl@athena.chicago.il.us>
+ * Hui Huang <hui.huang@nokia.com>
+ * Daisy Chang <daisyc@us.ibm.com>
+ * Sridhar Samudrala <sri@us.ibm.com>
+ */
+
+/* This is a userspace test application for the SCTP kernel
+ * implementation state machine. It is vaguely inspired by Stevens'
+ * program "sock".
+ *
+ * It has the limited ability to send messages and to listen for messages
+ * sent via SCTP.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+//#define _GNU_SOURCE
+#include <getopt.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <sys/poll.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <net/if.h>
+#include <netinet/sctp.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include "sctp_darn.h"
+
+char *TCID = __FILE__;
+int TST_TOTAL = 1;
+int TST_CNT = 0;
+
+#define GEN_DATA_FIRST 0x21
+#define GEN_DATA_LAST 0x7e
+
+/* Display an IPv4 address in readable format. */
+#define NIPQUAD(addr) \
+ ((unsigned char *)&addr)[0], \
+ ((unsigned char *)&addr)[1], \
+ ((unsigned char *)&addr)[2], \
+ ((unsigned char *)&addr)[3]
+
+/* Display an IPv6 address in readable format. */
+#define NIP6(addr) \
+ ntohs((addr).s6_addr16[0]), \
+ ntohs((addr).s6_addr16[1]), \
+ ntohs((addr).s6_addr16[2]), \
+ ntohs((addr).s6_addr16[3]), \
+ ntohs((addr).s6_addr16[4]), \
+ ntohs((addr).s6_addr16[5]), \
+ ntohs((addr).s6_addr16[6]), \
+ ntohs((addr).s6_addr16[7])
+
+/* These are the global options. */
+char *local_host = NULL;
+int local_port = 0;
+char *remote_host = NULL;
+int remote_port = 0;
+command_t command = COMMAND_NONE;
+struct sockaddr *bindx_add_addrs = NULL;
+int bindx_add_count = 0;
+struct sockaddr *bindx_rem_addrs = NULL;
+int bindx_rem_count = 0;
+struct sockaddr *connectx_addrs = NULL;
+int connectx_count = 0;
+int interactive_mode = 0;
+int poll_skn = 0;
+int nonblocking = 0;
+int opt_space = 0;
+char gen_data = GEN_DATA_FIRST;
+char *inter_outbuf = NULL;
+int inter_outlen = 0;
+int inter_sk = 0;
+int poll_snd_size = 0;
+int use_poll = 0;
+int socket_type = SOCK_SEQPACKET;
+sctp_assoc_t associd = 0;
+int echo = 0;
+char *interface = "eth0";
+int if_index = 0;
+sockaddr_storage_t remote_addr;
+sa_family_t ra_family; /* What family is remote_addr? */
+int ra_len = 0; /* How long is remote_addr? */
+void *ra_raw; /* This is the addr part of remote_addr. */
+int new_connection = 1;
+
+enum inter_cmd_num {
+ INTER_SND = 0,
+ INTER_RCV,
+ INTER_SNDBUF,
+ INTER_RCVBUF,
+ INTER_BINDX_ADD,
+ INTER_BINDX_REM,
+ INTER_SET_PRIM,
+ INTER_SET_PEER_PRIM,
+ INTER_SHUTDOWN,
+ INTER_ABORT,
+ INTER_NODELAY,
+ INTER_MAXSEG,
+ INTER_HEARTBEAT,
+ INTER_GET_STATS
+};
+
+enum shutdown_type {
+ SHUTDOWN_ABORT = 0,
+ SHUTDOWN_SHUTDOWN
+};
+
+struct inter_entry {
+ char *cmd;
+ int cmd_num;
+};
+
+struct inter_entry inter_commands[] = {
+ {"snd", INTER_SND},
+ {"rcv", INTER_RCV},
+ {"sndbuf", INTER_SNDBUF},
+ {"rcvbuf", INTER_RCVBUF},
+ {"bindx-add", INTER_BINDX_ADD},
+ {"bindx-rem", INTER_BINDX_REM},
+ {"primary", INTER_SET_PRIM},
+ {"peer_primary", INTER_SET_PEER_PRIM},
+ {"shutdown", INTER_SHUTDOWN},
+ {"abort", INTER_ABORT},
+ {"nodelay", INTER_NODELAY},
+ {"maxseg", INTER_MAXSEG},
+ {"heartbeat", INTER_HEARTBEAT},
+ {"stats", INTER_GET_STATS},
+ {NULL, -1},
+};
+
+#define POLL_SK_MAX 256 /* The max number of sockets to select/poll. */
+int poll_sks[POLL_SK_MAX]; /* The array for using select(). */
+struct pollfd poll_fds[POLL_SK_MAX]; /* The array for using poll(). */
+#define POLL_SND_SIZE 16384 /* Default message size in the poll mode. */
+
+
+struct sockaddr *append_addr(const char *parm, struct sockaddr *addrs,
+ int *ret_count) ;
+int build_endpoint(char *argv0, int portnum);
+static int parse_inter_commands(char *, char *, int);
+static void snd_func(char *);
+static void sndbuf_func(char *, int, int, int);
+static void rcvbuf_func(char *, int, int, int);
+static struct sockaddr *get_bindx_addr(char *, int *);
+static int bindx_func(char *, int, struct sockaddr *, int, int, int);
+static int connectx_func(char *, int, struct sockaddr *, int);
+static void primary_func(char *, int, char *, int);
+static void peer_primary_func(char *, int, char *, int);
+static void spp_hb_demand_func(char *, int, char *, int);
+static int nodelay_func(char *, int, int val, int set);
+static int maxseg_func(char *, int, int val, int set);
+static int shutdown_func(char *argv0, int *skp, int shutdown_type);
+static int get_assocstats_func(int, sctp_assoc_t);
+static int test_sk_for_assoc(int sk, sctp_assoc_t assoc_id);
+static char * gen_message(int);
+static sctp_assoc_t test_recv_assoc_change(int);
+static sctp_assoc_t test_verify_assoc_change(struct msghdr *);
+void print_addr_buf(void * laddrs, int n_laddrs);
+int print_sockaddr(struct sockaddr *sa_addr);
+
+int
+main(int argc, char *argv[]) {
+ int sk = -1;
+ int error = 0;
+ int i;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ parse_arguments(argc, argv);
+
+ switch(command) {
+ case COMMAND_NONE:
+ fprintf(stderr, "%s: Please specify a command.\n",
+ argv[0]);
+ exit(1);
+ break;
+ case COMMAND_LISTEN:
+ sk = build_endpoint(argv[0], local_port);
+ error = command_listen(argv[0], sk);
+ break;
+ case COMMAND_SEND:
+ sk = build_endpoint(argv[0], local_port);
+ error = command_send(argv[0], &sk);
+ break;
+ case COMMAND_POLL:
+ if (use_poll) {
+ for (i = 0; i < poll_skn; i++) {
+ poll_fds[i].fd = build_endpoint(argv[0],
+ local_port + i);
+ }
+ } else {
+ for (i = 0; i < poll_skn; i++) {
+ poll_sks[i] = build_endpoint(argv[0],
+ local_port + i);
+ }
+ }
+ error = command_poll(argv[0]);
+ break;
+ default:
+ fprintf(stderr, "%s: illegal command %d\n",
+ argv[0], command);
+ exit(1);
+ }
+
+ /* Shut down the link. */
+ if (COMMAND_POLL != command) {
+ close(sk);
+ } else {
+ /* Shutdown all links. */
+ if (use_poll) {
+ for (i = 0; i < poll_skn; i++) {
+ close(poll_fds[i].fd);
+ }
+ } else {
+ for (i = 0; i < poll_skn; i++) {
+ close(poll_sks[i]);
+ }
+ }
+ }
+
+ exit(error);
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+void
+parse_arguments(int argc, char *argv[]) {
+ int option_index = 0;
+ int c;
+ struct sockaddr *tmp_addrs = NULL;
+
+ static struct option long_options[] = {
+ {"local", 1, 0, 1},
+ {"local-port", 1, 0, 2},
+ {"remote", 1, 0, 3},
+ {"remote-port", 1, 0, 4},
+ {"listen", 0, 0, 10},
+ {"send", 0, 0, 11},
+ {"bindx-add", 1, 0, 15},
+ {"bindx-rem", 1, 0, 16},
+ {"use-poll", 0, 0, 20},
+ {"echo", 0, 0, 'e'},
+ {"interface", optional_argument, 0, 5,},
+ {"connectx", 1, 0, 17},
+ {0, 0, 0, 0}
+ };
+
+ /* Parse the arguments. */
+ while (1) {
+ c = getopt_long (argc, argv, "B:H:IP:b:h:i:p:lm:nstz:ec:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ printf("option %s", long_options[option_index].name);
+ if (optarg) {
+ printf(" with arg %s", optarg);
+ }
+ printf("\n");
+ break;
+ case 1: /* local host */
+ case 'H':
+ local_host = optarg;
+ break;
+ case 2: /* local port */
+ case 'P':
+ local_port = atoi(optarg);
+ break;
+ case 3: /* remote host */
+ case 'h':
+ remote_host = optarg;
+ break;
+ case 4: /* remote port */
+ case 'p':
+ remote_port = atoi(optarg);
+ break;
+ case 5: /* interface for sin6_scope_id */
+ if (optarg)
+ interface = optarg;
+ if_index = if_nametoindex(interface);
+ if (!if_index) {
+ printf("Interface %s unknown\n", interface);
+ exit(1);
+ }
+ break;
+ /* COMMANDS */
+ case 10: /* listen */
+ case 'l':
+ if (command) {
+ fprintf(stderr,
+ "%s: pick ONE of listen or send\n",
+ argv[0]);
+ exit(1);
+ } else {
+ command = COMMAND_LISTEN;
+ }
+ break;
+
+ case 11: /* send */
+ case 's':
+ if (command) {
+ fprintf(stderr,
+ "%s: pick ONE of listen or send\n",
+ argv[0]);
+ exit(1);
+ } else {
+ command = COMMAND_SEND;
+ }
+ break;
+
+ case 15: /* bindx_add */
+ case 'B':
+ tmp_addrs =
+ append_addr(optarg, bindx_add_addrs,
+ &bindx_add_count);
+ if (NULL == tmp_addrs) {
+ /* We have no memory, so keep fprintf()
+ * from trying to allocate more.
+ */
+ fprintf(stderr, "No memory to add ");
+ fprintf(stderr, "%s\n", optarg);
+ exit(2);
+ }
+ bindx_add_addrs = tmp_addrs;
+
+ break;
+
+ case 16: /* bindx_rem */
+ case 'b':
+ tmp_addrs =
+ append_addr(optarg, bindx_rem_addrs,
+ &bindx_rem_count);
+ if (NULL == tmp_addrs) {
+ /* We have no memory, so keep fprintf()
+ * from trying to allocate more.
+ */
+ fprintf(stderr, "No memory to add ");
+ fprintf(stderr, "%s\n", optarg);
+ exit(2);
+ }
+ bindx_rem_addrs = tmp_addrs;
+ break;
+ case 17: /* connectx */
+ case 'c':
+ tmp_addrs =
+ append_addr(optarg, connectx_addrs,
+ &connectx_count);
+ if (NULL == tmp_addrs) {
+ /* We have no memory, so keep fprintf()
+ * from trying to allocate more.
+ */
+ fprintf(stderr, "No memory to add ");
+ fprintf(stderr, "%s\n", optarg);
+ exit(2);
+ }
+ connectx_addrs = tmp_addrs;
+ break;
+ case 20: /* use-poll */
+ use_poll = 1;
+ break;
+ case 'I':
+ interactive_mode = 1;
+ break;
+ case 'i':
+ command = COMMAND_POLL;
+ poll_skn = atoi(optarg);
+ if (poll_skn <= 0 || poll_skn > POLL_SK_MAX) {
+ fprintf(stderr, "Too many sockets for ");
+ fprintf(stderr, "for polling\n");
+ exit(2);
+ }
+ break;
+ case 'm':
+ opt_space = atoi(optarg);
+ break;
+ case 'n':
+ nonblocking = 1;
+ break;
+ case 't':
+ socket_type = SOCK_STREAM;
+ break;
+ case 'z':
+ poll_snd_size = atoi(optarg);
+ if (poll_snd_size <= 0) {
+ fprintf(stderr, "Bad message size.\n");
+ exit(2);
+ }
+ break;
+ case 'e':
+ echo = 1;
+ break;
+ case '?':
+ usage(argv[0]);
+ exit(1);
+
+ default:
+ printf ("%s: unrecognized option 0%c\n",
+ argv[0], c);
+ 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 (NULL == local_host) {
+ fprintf(stderr, "%s: You MUST provide a local host.\n",
+ argv[0]);
+ usage(argv[0]);
+ exit(1);
+ }
+
+ if (command == COMMAND_SEND && NULL == remote_host
+ && connectx_count == 0) {
+ fprintf(stderr, "%s: You MUST provide a remote host for sending.\n",
+ argv[0]);
+ 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);
+ }
+} /* parse_arguments() */
+
+/* Set up the local endpoint. */
+int
+build_endpoint(char *argv0, int portnum)
+{
+ int retval;
+ struct hostent *hst;
+ sockaddr_storage_t local_addr;
+ sa_family_t la_family; /* What family is local_addr? */
+ int la_len; /* How long is local_addr? */
+ void *la_raw; /* This is the addr part of local_addr. */
+ int error;
+ struct sctp_event_subscribe subscribe;
+
+ /* Get the transport address for the local host name. */
+ hst = gethostbyname(local_host);
+ if (hst == NULL) {
+ hst = gethostbyname2(local_host, AF_INET6);
+ }
+
+ if (hst == NULL || hst->h_length < 1) {
+ fprintf(stderr, "%s: bad hostname: %s\n", argv0, local_host);
+ exit(1);
+ }
+
+ la_family = hst->h_addrtype;
+ switch (la_family) {
+ case AF_INET:
+ la_len = sizeof(local_addr.v4);
+ la_raw = &local_addr.v4.sin_addr;
+ local_addr.v4.sin_port = htons(portnum);
+ local_addr.v4.sin_family = AF_INET;
+ break;
+ case AF_INET6:
+ la_len = sizeof(local_addr.v6);
+ la_raw = &local_addr.v6.sin6_addr;
+ local_addr.v6.sin6_port = htons(portnum);
+ local_addr.v6.sin6_family = AF_INET6;
+ local_addr.v6.sin6_scope_id = if_index;
+ break;
+ default:
+ fprintf(stderr, "Invalid address type.\n");
+ exit(1);
+ break;
+ }
+ memcpy(la_raw, hst->h_addr_list[0], hst->h_length);
+
+ /* Create the local endpoint. */
+ retval = socket(la_family, socket_type, IPPROTO_SCTP);
+ if (retval < 0) {
+ fprintf(stderr, "%s: failed to create socket: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+
+ if (SOCK_SEQPACKET == socket_type) {
+ memset(&subscribe, 0, sizeof(subscribe));
+ subscribe.sctp_data_io_event = 1;
+ subscribe.sctp_association_event = 1;
+ error = setsockopt(retval, SOL_SCTP, SCTP_EVENTS,
+ (char *)&subscribe, sizeof(subscribe));
+ if (error) {
+ fprintf(stderr, "SCTP_EVENTS: error: %d\n", error);
+ exit(1);
+ }
+ }
+
+ /* Bind this socket to the test port. */
+ error = bind(retval, &local_addr.sa, la_len);
+ if (error != 0) {
+ fprintf(stderr, "%s: can not bind to %s:%d: %s.\n",
+ argv0, local_host, portnum,
+ strerror(errno));
+ exit(1);
+ }
+
+ /* Do we need to do bindx() to add any additional addresses? */
+ if (bindx_add_addrs) {
+ if (0 != bindx_func(argv0, retval, bindx_add_addrs,
+ bindx_add_count, SCTP_BINDX_ADD_ADDR, portnum)) {
+ fprintf(stderr, "bindx_func (add) failed.\n");
+ exit(1);
+ }
+ } /* if (bindx_add_addrs) */
+
+ /* Do we need to do bindx() to remove any bound addresses? */
+ if (bindx_rem_addrs) {
+ if (0 != bindx_func(argv0, retval, bindx_rem_addrs,
+ bindx_rem_count, SCTP_BINDX_REM_ADDR, portnum)) {
+ fprintf(stderr, "bindx_func (remove) failed.\n");
+ exit(1);
+ }
+ } /* if (bindx_rem_addrs) */
+
+ /* Do we want to run in the non-blocking mode? */
+ if (nonblocking) {
+ error = fcntl(retval, F_SETFL, O_NONBLOCK);
+ if (error != 0) {
+ fprintf(stderr, "%s: error fcntl: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (opt_space) {
+ sndbuf_func(argv0, retval, opt_space, 1);
+ rcvbuf_func(argv0, retval, opt_space, 1);
+ }
+
+ return retval;
+
+} /* build_endpoint() */
+
+/* Convenience structure to determine space needed for cmsg. */
+typedef union {
+ struct sctp_initmsg init;
+ struct sctp_sndrcvinfo sndrcvinfo;
+} _sctp_cmsg_data_t;
+
+
+/* Listen on the socket, printing out anything that arrives. */
+int
+command_listen(char *argv0, int sk)
+{
+ char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
+ struct iovec iov;
+ struct msghdr inmessage;
+ sockaddr_storage_t msgname;
+ char message[REALLY_BIG];
+ int done = 0;
+ int error;
+ int c;
+ int recvsk = 0;
+
+ /* Mark sk as being able to accept new associations */
+ error = listen(sk, 5);
+ if (error != 0) {
+ printf("\n\n\t\tlisten Failure: %s.\n\n\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ if (nonblocking) {
+ if (!interactive_mode) {
+ printf("Use -I for interactive mode with");
+ printf(" -n nonblocking\n");
+ exit(1);
+ }
+ }
+
+ /* Initialize the global value for interactive mode functions. */
+ if (interactive_mode) {
+ inter_sk = sk;
+ }
+
+ /* Initialize inmessage with enough space for DATA... */
+ memset(&inmessage, 0, sizeof(inmessage));
+ if ((iov.iov_base = malloc(REALLY_BIG)) == NULL) {
+ printf("%s: Can't allocate memory.\n", argv0);
+ 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);
+ inmessage.msg_name = &msgname;
+ inmessage.msg_namelen = sizeof(msgname);
+
+ printf("%s listening...\n", argv0);
+ /* Get the messages sent */
+ done = 0;
+ while (!done) {
+ if (interactive_mode) {
+ /* Read from the user. */
+ if (remote_host) {
+ printf("%s:%d-%s:%d Interactive mode> ",
+ local_host, local_port, remote_host,
+ remote_port);
+ } else {
+ printf("%s:%d-", local_host, local_port);
+ if (associd) {
+ print_sockaddr(&remote_addr.sa);
+ } else {
+ printf("?:%d", remote_port);
+ }
+ printf(" Interactive mode> ");
+ }
+ fflush(stdout);
+ if (NULL == fgets(message, REALLY_BIG, stdin)) {
+ done = 1;
+ continue;
+ }
+
+ if (0 <= (c = parse_inter_commands(argv0, message,
+ 0))) {
+ if (INTER_RCV != c) {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ }
+
+ if (socket_type == SOCK_STREAM) {
+ socklen_t len = 0;
+
+ if (!recvsk) {
+ if ((recvsk = accept(sk, NULL, &len)) < 0) {
+ fprintf(stderr, "%s: error: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+ }
+
+ } else {
+ recvsk = sk;
+ }
+
+ error = recvmsg(recvsk, &inmessage, MSG_WAITALL);
+ if (error < 0) {
+ if (nonblocking && (EAGAIN == errno)) {
+ error = 0;
+ continue;
+ }
+
+ if (socket_type == SOCK_STREAM) {
+ if (ENOTCONN != errno)
+ break;
+ printf("No association is present now!!\n");
+ close(recvsk);
+ recvsk = 0;
+ continue;
+ }
+ break;
+ }
+
+ /* Update the associd when a notification is received on a
+ * UDP-style socket.
+ */
+ if (inmessage.msg_flags & MSG_NOTIFICATION)
+ associd = test_verify_assoc_change(&inmessage);
+
+ if (echo) {
+ if( !(MSG_NOTIFICATION & inmessage.msg_flags)) {
+ if (sendto(recvsk, inmessage.msg_iov->iov_base,
+ error, 0,
+ (struct sockaddr *)&msgname,
+ sizeof(msgname)) == -1) {
+ fprintf(stderr, "%s: error: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+ }
+ }
+
+ test_print_message(sk, &inmessage, error);
+
+ inmessage.msg_control = incmsg;
+ inmessage.msg_controllen = sizeof(incmsg);
+ inmessage.msg_name = &msgname;
+ inmessage.msg_namelen = sizeof(msgname);
+ iov.iov_len = REALLY_BIG;
+
+ /* Verify that the association is no longer present. */
+ if (0 != test_sk_for_assoc(recvsk, associd)) {
+ printf("No association is present now!!\n");
+ if (socket_type == SOCK_STREAM) {
+ close(recvsk);
+ recvsk = 0;
+ }
+ }
+ }
+
+ if (error < 0) {
+ fprintf(stderr, "%s: error: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+
+ return error;
+
+} /* command_listen() */
+
+/* Read lines from stdin and send them to the socket. */
+int
+command_send(char *argv0, int *skp)
+{
+ struct msghdr outmsg;
+ struct iovec iov;
+ int done = 0;
+ char message[REALLY_BIG];
+ struct hostent *hst;
+ int c;
+ struct sockaddr *addrs;
+ int msglen;
+ int error = 0;
+ int sk = *skp;
+
+ /* Set up the destination. */
+ if (remote_host != NULL) {
+ hst = gethostbyname(remote_host);
+ if (hst == NULL) {
+ hst = gethostbyname2(remote_host, AF_INET6);
+ }
+
+ if (hst == NULL || hst->h_length < 1) {
+ fprintf(stderr, "%s: bad hostname: %s\n",
+ argv0, remote_host);
+ exit(1);
+ }
+
+ ra_family = hst->h_addrtype;
+ switch (ra_family) {
+ case AF_INET:
+ ra_len = sizeof(remote_addr.v4);
+ ra_raw = &remote_addr.v4.sin_addr;
+ remote_addr.v4.sin_port = htons(remote_port);
+ remote_addr.v4.sin_family = AF_INET;
+ break;
+ case AF_INET6:
+ ra_len = sizeof(remote_addr.v6);
+ ra_raw = &remote_addr.v6.sin6_addr;
+ remote_addr.v6.sin6_port = htons(remote_port);
+ remote_addr.v6.sin6_family = AF_INET6;
+ remote_addr.v6.sin6_scope_id = if_index;
+ break;
+ default:
+ fprintf(stderr, "Invalid address type.\n");
+ exit(1);
+ break;
+ }
+ memcpy(ra_raw, hst->h_addr_list[0], hst->h_length);
+ }
+
+ /* Initialize the global value for interactive mode functions. */
+ if (interactive_mode) {
+ inter_sk = sk;
+ }
+
+ printf("%s ready to send...\n", argv0);
+ while (!done) {
+ /* Read from the user. */
+ if (remote_host) {
+ if (interactive_mode) {
+ printf("%s:%d-%s:%d Interactive mode> ",
+ local_host, local_port, remote_host,
+ remote_port);
+ } else {
+ printf("%s:%d-%s:%d> ",
+ local_host, local_port,
+ remote_host, remote_port);
+ }
+ } else {
+ printf("%s:%d-", local_host, local_port);
+ if (associd) {
+ print_sockaddr(&remote_addr.sa);
+ } else {
+ printf("XXXXXX:%d", remote_port);
+ }
+ if (interactive_mode) {
+ printf(" Interactive mode> ");
+ } else {
+ printf("> ");
+ }
+ }
+ fflush(stdout);
+ if (NULL == fgets(message, REALLY_BIG, stdin)) {
+ done = 1;
+ continue;
+ }
+
+ if (interactive_mode) {
+ /* This is the send only agent. */
+ if (0 <= (c = parse_inter_commands(argv0, message,
+ 1))) {
+ if (INTER_SND == c) {
+ iov.iov_base = inter_outbuf;
+ msglen = inter_outlen;
+ iov.iov_len = msglen;
+
+ } else {
+ continue;
+ }
+
+ } else {
+ continue;
+ }
+ } else {
+ /* Send to our neighbor. */
+ msglen = strlen(message) + 1;
+ iov.iov_len = msglen;
+ }
+
+ /* For a UDP-style socket, verify if an existing association
+ * has gone. If so, receive the pending SCTP_ASSOC_CHANGE
+ * notification.
+ */
+ if ((SOCK_SEQPACKET == socket_type) && associd &&
+ (0 != test_sk_for_assoc(sk, associd))) {
+ associd = test_recv_assoc_change(sk);
+ printf("Old association gone, Starting a new one!\n");
+ new_connection = 1;
+ }
+
+ if (new_connection && connectx_count != 0) {
+ /* Do a sctp_connectx() to establish a connection. */
+ error = connectx_func(argv0, sk, connectx_addrs,
+ connectx_count);
+ if (0 != error) {
+ if (error == -2) {
+ printf("Connection refused\n");
+ if (SOCK_SEQPACKET == socket_type) {
+ associd = test_recv_assoc_change(sk);
+ }
+ continue;
+ }
+ fprintf(stderr, "connectx failed.\n");
+ exit(1);
+ }
+ if (SOCK_SEQPACKET == socket_type) {
+ associd = test_recv_assoc_change(sk);
+ } else {
+ associd = 1;
+ }
+ int rc = sctp_getpaddrs(sk, associd, &addrs);
+ if (0 >= rc) {
+ if (rc == 0) {
+ fprintf(stderr, "sctp_getpaddrs failed, no peers.\n");
+ } else {
+ fprintf(stderr, "sctp_getpaddrs failed %s(%d).\n", strerror(errno), errno);
+ }
+ exit(1);
+ }
+ printf("New connection, peer addresses\n");
+ print_addr_buf(addrs, rc);
+ ra_family = addrs[0].sa_family;
+ switch (ra_family) {
+ case AF_INET:
+ ra_len = sizeof(remote_addr.v4);
+ break;
+ case AF_INET6:
+ ra_len = sizeof(remote_addr.v6);
+ break;
+ default:
+ fprintf(stderr, "Invalid address type.\n");
+ exit(1);
+ }
+ memcpy(&remote_addr, &addrs[0], ra_len);
+ sctp_freepaddrs(addrs);
+ new_connection = 0;
+ }
+
+ do {
+ if (SOCK_SEQPACKET == socket_type ||
+ (connectx_count == 0 && new_connection)) {
+ /* Initialize the message struct we use to pass
+ * messages to the remote socket.
+ */
+ if (!interactive_mode) {
+ iov.iov_base = message;
+ iov.iov_len = msglen;
+ }
+ outmsg.msg_iov = &iov;
+ outmsg.msg_iovlen = 1;
+ outmsg.msg_control = NULL;
+ outmsg.msg_controllen = 0;
+ outmsg.msg_name = &remote_addr;
+ outmsg.msg_namelen = ra_len;
+ outmsg.msg_flags = 0;
+
+ error = sendmsg(sk, &outmsg, 0);
+ } else {
+ error = send(sk, message, msglen, 0);
+ if (error == -1 && errno == EPIPE) {
+ error = close(sk);
+ if (error != 0) {
+ fprintf(stderr, "close failed %s\n", strerror(errno));
+ exit(1);
+ }
+ *skp = sk = build_endpoint(argv0, local_port);
+ break;
+ }
+ }
+
+ if (error != msglen) {
+ fprintf(stderr, "%s: error: %s.\n",
+ argv0, strerror(errno));
+ if (nonblocking && EAGAIN == errno) {
+ if (interactive_mode) {
+ break;
+ }
+ continue;
+ }
+ exit(1);
+ } else {
+ break;
+ }
+ } while (error != msglen);
+
+ /* If this is the first message sent over a UDP-style socket,
+ * get the associd from the SCTP_ASSOC_CHANGE notification.
+ */
+ if ((SOCK_SEQPACKET == socket_type) && (0 == associd))
+ associd = test_recv_assoc_change(sk);
+
+ /* Verify there is no association. */
+ if (0 != test_sk_for_assoc(sk, associd)) {
+ printf("No association is present now!!\n");
+ new_connection = 1;
+ } else {
+ if (new_connection) {
+ int rc = sctp_getpaddrs(sk, associd, &addrs);
+ if (0 >= rc) {
+ if (rc == 0) {
+ fprintf(stderr, "sctp_getpaddrs failed, no peers.\n");
+ } else {
+ fprintf(stderr, "sctp_getpaddrs failed %s(%d).\n", strerror(errno), errno);
+ }
+ exit(1);
+ }
+ printf("New connection, peer addresses\n");
+ print_addr_buf(addrs, rc);
+ sctp_freepaddrs(addrs);
+ new_connection = 0;
+ }
+ }
+
+ /* Clean up. */
+ if (interactive_mode) {
+ free(inter_outbuf);
+ inter_outbuf = NULL;
+ }
+ } /* while(!done) */
+
+ return error;
+
+} /* command_send() */
+
+/* Listen on the array of sockets, printing out anything that arrives. */
+int
+command_poll(char *argv0)
+{
+ char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
+ struct iovec iov;
+ struct msghdr inmessage;
+ int done = 0;
+ int error = 0;
+ int max_fd, i, ret;
+ int size;
+ fd_set *ibitsp = NULL;
+ fd_set *obitsp = NULL;
+ fd_set *xbitsp = NULL;
+
+ struct msghdr outmsg;
+ struct hostent *hst;
+ int msglen;
+ int temp_fd, temp_set;
+
+
+
+ /* If a remote host is specified, initialize the destination. */
+ if (remote_host) {
+ /* Set up the destination. */
+ hst = gethostbyname(remote_host);
+ if (hst == NULL) {
+ hst = gethostbyname2(remote_host, AF_INET6);
+ }
+
+ if (hst == NULL || hst->h_length < 1) {
+ fprintf(stderr, "%s: bad hostname: %s\n",
+ argv0, remote_host);
+ exit(1);
+ }
+
+ ra_family = hst->h_addrtype;
+ switch (ra_family) {
+ case AF_INET:
+ ra_len = sizeof(remote_addr.v4);
+ ra_raw = &remote_addr.v4.sin_addr;
+ remote_addr.v4.sin_port = htons(remote_port);
+ remote_addr.v4.sin_family = AF_INET;
+ break;
+ case AF_INET6:
+ ra_len = sizeof(remote_addr.v6);
+ ra_raw = &remote_addr.v6.sin6_addr;
+ remote_addr.v6.sin6_port = htons(remote_port);
+ remote_addr.v6.sin6_family = AF_INET6;
+ remote_addr.v6.sin6_scope_id = if_index;
+ break;
+ default:
+ fprintf(stderr, "Invalid address type.\n");
+ exit(1);
+ break;
+ }
+ memcpy(ra_raw, hst->h_addr_list[0], hst->h_length);
+
+ /* Initialize the message struct we use to pass messages to
+ * the remote socket.
+ */
+ outmsg.msg_iov = &iov;
+ outmsg.msg_iovlen = 1;
+ outmsg.msg_control = NULL;
+ outmsg.msg_controllen = 0;
+ outmsg.msg_name = &remote_addr;
+ outmsg.msg_namelen = ra_len;
+ outmsg.msg_flags = 0;
+ }
+
+
+ max_fd = -1;
+
+ /* Set all of the sockets to be ready for listening. */
+ if (use_poll) {
+ for (i = 0; i < poll_skn; i++) {
+ error = listen(poll_fds[i].fd, 1);
+ if (error != 0) {
+ printf("%s: Listen failed on socket number ",
+ argv0);
+ printf("%d: %s.\n", i, strerror(errno));
+ exit(1);
+ }
+ }
+ printf("%s listening...\n", argv0);
+ } else {
+ for (i = 0; i < poll_skn; i++) {
+ error = listen(poll_sks[i], 1);
+ if (error != 0) {
+ printf("%s: Listen failed on socket number ",
+ argv0);
+ printf("%d: %s.\n", i, strerror(errno));
+ exit(1);
+ }
+ if (poll_sks[i] > max_fd) {
+ max_fd = poll_sks[i];
+ }
+ }
+ printf("%s listening...\n", argv0);
+
+ size = howmany(max_fd + 1, NFDBITS) * sizeof(fd_mask);
+ if ((ibitsp = (fd_set *)malloc(size)) == NULL) {
+ printf("%s: Can't allocate memory.\n", argv0);
+ exit(1);
+ }
+ if ((obitsp = (fd_set *)malloc(size)) == NULL) {
+ printf("%s: Can't allocate memory.\n", argv0);
+ exit(1);
+ }
+ if ((xbitsp = (fd_set *)malloc(size)) == NULL) {
+ printf("%s: Can't allocate memory.\n", argv0);
+ exit(1);
+ }
+ 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) {
+ printf("%s: Can't allocate memory.\n", argv0);
+ 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);
+
+
+ done = 0;
+ /* Set the default send message size. */
+ if (!poll_snd_size) {
+ poll_snd_size = POLL_SND_SIZE;
+ }
+
+ while (!done) {
+
+ if (use_poll) {
+ for (i = 0; i < poll_skn; i++) {
+ poll_fds[i].events = POLLIN;
+ }
+ if (remote_host) {
+ /* Poll output on the first socket. */
+ poll_fds[0].events |= POLLOUT;
+ }
+
+ if ((ret = poll(poll_fds, poll_skn, -1))) {
+ if (ret == -1) {
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < poll_skn; i++) {
+ FD_SET(poll_sks[i], ibitsp);
+ FD_SET(poll_sks[i], xbitsp);
+ }
+ if (remote_host) {
+ /* Only select output on the first socket. */
+ FD_SET(poll_sks[0], obitsp);
+ }
+
+
+ if ((ret = select(max_fd + 1, ibitsp, obitsp, xbitsp,
+ (struct timeval *)0)) < 0) {
+ if (ret == -1) {
+ break;
+ }
+ }
+
+ }
+
+ if (remote_host) {
+ if (use_poll) {
+ temp_set = poll_fds[0].revents & POLLOUT;
+ temp_fd = poll_fds[0].fd;
+ } else {
+ temp_set = FD_ISSET(poll_sks[0], obitsp);
+ temp_fd = poll_sks[0];
+ }
+
+ if (temp_set) {
+ inter_outbuf = gen_message(poll_snd_size);
+ if (!inter_outbuf) {
+ fprintf(stderr,
+ "Cannot allocate out message.\n");
+ exit(1);
+ }
+ iov.iov_base = inter_outbuf;
+ msglen = poll_snd_size;
+ iov.iov_len = msglen;
+
+ error = sendmsg(temp_fd, &outmsg, 0);
+ fprintf(stderr,
+ "sent a message, msglen = %d\n",
+ msglen);
+
+ if (error != msglen) {
+ fprintf(stderr, "%s: error: %s.\n",
+ argv0, strerror(errno));
+ if ((!nonblocking) ||
+ (EAGAIN != errno)) {
+ exit(1);
+ }
+ }
+
+ /* Clean up. */
+ free(inter_outbuf);
+ inter_outbuf = NULL;
+ }
+
+ } /* while(!done) */
+
+ for (i = 0; !done && (i < poll_skn); i++) {
+ if (use_poll) {
+ temp_set = poll_fds[i].revents & POLLIN;
+ temp_fd = poll_fds[i].fd;
+ } else {
+ temp_set = FD_ISSET(poll_sks[i], ibitsp);
+ temp_fd = poll_sks[i];
+ }
+ if (temp_set) {
+ error = recvmsg(temp_fd, &inmessage,
+ MSG_WAITALL);
+ if (error < 0) {
+ if ((EAGAIN == errno)) {
+ error = 0;
+ continue;
+ }
+ else {
+ fprintf(stderr,
+ "%s: error: %s.\n",
+ argv0,
+ strerror(errno));
+ exit(1);
+ }
+ }
+ test_print_message(temp_fd, &inmessage, error);
+ inmessage.msg_control = incmsg;
+ inmessage.msg_controllen = sizeof(incmsg);
+ iov.iov_len = REALLY_BIG;
+ }
+
+ /* Update the associd when a notification is received
+ * on a UDP-style socket.
+ */
+ if (inmessage.msg_flags & MSG_NOTIFICATION)
+ associd = test_verify_assoc_change(&inmessage);
+
+ /* Verify there is no association. */
+ if (0 != test_sk_for_assoc(poll_sks[i], associd)) {
+ printf("No association is present in sk "
+ "No.%d now!!\n",i);
+ }
+ }
+
+ }
+
+ if (!use_poll) {
+ free(ibitsp);
+ free(obitsp);
+ free(xbitsp);
+ }
+
+ return error;
+
+} /* command_poll() */
+
+/********************************************************************
+ * 3rd Level Abstractions
+ ********************************************************************/
+
+#define FPS(arg) fprintf(stderr, arg)
+
+void
+usage(char *argv0)
+{
+ /*
+ * The bindx options, --bindx-add and --bindx-rem, are added to
+ *
+ * 1. provide first testcases for the new bindx system call
+ *
+ * 2. continue to grow sctp_darn with more functions and
+ * features so it will be equivalent to the "sock" tool for
+ * TCP as for SCTP.
+ *
+ * FIXME -
+ *
+ * It is not very effective to use these two options in the
+ * current command line mode of sctp_darn. For example, the
+ * --bindx-rem option can only be used in conjunction with the
+ * --bindx-add simply to test the function in the kernel
+ * path. Ideally, bindx needs to be tested by a tool which
+ * provides an interactive mode for users to change parameters
+ * and configuration dynamically with existing endpoints and
+ * associations.
+ */
+ fprintf(stderr, "Usage: %s -H <localhost> -P <localport> "
+ "[-h <remotehost>] [-p <remoteport>] -l|s\n"
+ " -H, --local\t\tspecify one of the local addresses,\n"
+ " -P, --local-port\tspecify the port number for local addresses,\n"
+ " -h, --remote\t\tspecify the peer address,\n"
+ " -p, --remote-port\tspecify the port number for the peer address,\n"
+ " -l, --listen\t\tprint messages received from the peer,\n"
+ " -s, --send\t\tsend messages to the peer,\n"
+ " -B, --bindx-add"
+ "\tadd the specified address(es) as additional bind\n"
+ "\t\t\taddresses to the local socket. Multiple addresses can\n"
+ "\t\t\tbe specified by using this argument multiple times.\n"
+ "\t\t\tFor example, '-B 10.0.0.1 -B 20.0.0.2'.\n"
+ " -b, --bindx-rem"
+ "\tremove the specified address(es) from the bind\n"
+ "\t\t\taddresses of the local socket. Multiple addresses can\n"
+ "\t\t\tbe specified by using this argument multiple times.\n"
+ "\t\t\tFor example, '-b 10.0.0.1 -b 20.0.0.2'.\n"
+ " -c, --connectx"
+ "\t\tuse the specified address(es) for connection to the\n"
+ "\t\t\tpeer socket. Multiple addresses can be specified by\n"
+ "\t\t\tusing this argument multiple times.\n"
+ "\t\t\tFor example, '-c 10.0.0.1 -c 20.0.0.2'.\n"
+ "\t\t\tThis option is incompatible with the -h option.\n"
+ " -I\t\t\tuse the interactive mode.\n"
+ " -i\t\t\tsetup the specified number of endpoints by using the\n"
+ "\t\t\tspecified local host (-H) and local port (-P). The port\n"
+ "\t\t\tnumber will be incremented by one for each additional\n"
+ "\t\t\tendpoint. All of these endpoints will be listening.\n"
+ "\t\t\tIf a remote host (-h) and a remote port are also\n"
+ "\t\t\tspecified, the first endpoint will start sending fixed\n"
+ "\t\t\tsized messages to the remote host.\n"
+ " -m\t\t\tspecify the sockopt sndbuf/rcvbuf size.\n"
+ " -n\t\t\tset the socket(s) to be in the non-blocking mode.\n"
+ "\t\t\tcollect messages from stdin and deliver them to the\n"
+ "\t\t\tpeer,\n"
+ "--use-poll\t\tuse system call poll() for polling among the\n"
+ "\t\t\tnumber of endpoints specified by the -i option. Without\n"
+ "\t\t\tthis option, select() would be used as default.\n"
+ " -t\t\t\tuse SOCK_STREAM tcp-style sockets.\n"
+ " -z\t\t\tspecify the message size to be sent. The default\n"
+ "\t\t\tmessage size generated would be 16K.\n"
+ " --interface=\"ifname\"\tselect interface for sin6_scope_id.\n",
+ argv0);
+}
+
+
+/* This function checks messages to see if they are of type 'event'
+ * and if they are well-formed.
+ */
+int
+user_test_check_message(struct msghdr *msg,
+ int controllen,
+ sctp_cmsg_t event)
+{
+
+
+ if (msg->msg_controllen != controllen) {
+ fprintf(stderr,
+ "Got control structure of length %zu, not %d\n",
+ msg->msg_controllen, controllen);
+ exit(1);
+ }
+ if (controllen > 0 && event != CMSG_FIRSTHDR(msg)->cmsg_type) {
+ fprintf(stderr, "Wrong kind of event: %d, not %d\n",
+ CMSG_FIRSTHDR(msg)->cmsg_type, event);
+ exit(1);
+ }
+
+ return 1;
+
+} /* user_test_check_message() */
+
+/* Add another address represented as the string 'parm' to the list
+ * addrs. The argument count is the number of addrs on input and is
+ * adjusted for output.
+ */
+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;
+
+
+ if (!parm)
+ return NULL;
+ /* Get the entries for this host. */
+ hst4 = gethostbyname(parm);
+ hst6 = gethostbyname2(parm, AF_INET6);
+
+ if ((NULL == hst4 || hst4->h_length < 1)
+ && (NULL == hst6 || hst6->h_length < 1)) {
+ fprintf(stderr, "bad hostname: %s\n", parm);
+ 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);
+
+ aptr += sizeof(struct sockaddr_in6);
+ } /* for (loop through the new v6 addresses) */
+ }
+
+ finally:
+
+ *ret_count = count;
+
+ return new_addrs;
+
+} /* append_addr() */
+
+static int
+parse_inter_commands(char *argv0, char *input, int snd_only)
+{
+ int i;
+ char *p;
+ int len;
+ int set = 0;
+ int val;
+ struct sockaddr *tmp_addrs = NULL;
+
+
+ p = input;
+ if (*p == '?' || *p == '\n') {
+ printf("Interactive commands:\n");
+ printf("snd=<int> - Do a sendmsg with the specified");
+ printf(" length.\n");
+ printf("rcv=<int> - Do a recvmsg.");
+ printf("The length is ignored for now.\n");
+ printf("bindx-add=<addr> - Add a local address");
+ printf(" with bindx. \n");
+ printf("bindx-rem=<addr> - Remove a local address");
+ printf(" with bindx. \n");
+ printf("rcvbuf=<int> - Get/Set receive buffer size\n");
+ printf("sndbuf=<int> - Get/Set send buffer size.\n");
+ printf("primary=<addr> - Get/Set association's primary\n");
+ printf("peer_primary=addr- Set association's peer_primary\n");
+ printf("heartbeat=<addr> - Request a user initiated heartbeat\n");
+ printf("maxseg=<int> - Get/Set Maximum fragment size.\n");
+ printf("nodelay=<0|1> - Get/Set NODELAY option.\n");
+ printf("shutdown - Shutdown the association.\n");
+ printf("abort - Abort the association.\n");
+ printf("stats - Print GET_ASSOC_STATS (if available in kernel).\n");
+ printf("? - Help. Display this message.\n");
+ return -1;
+ }
+
+ for (i = 0; i < REALLY_BIG; i++) {
+ if (('=' == *p) ||
+ ('?' == *p) ||
+ ('\n' == *p)) {
+ if ('=' == *p) {
+ set = 1;
+ }
+ *p++ = '\0';
+ break;
+ }
+ p++;
+ }
+ if (i >= REALLY_BIG) {
+ printf("Invalid input.\n");
+ return -1;
+ }
+
+ i = 0;
+ while (NULL != inter_commands[i].cmd) {
+ if (!strcmp(input, inter_commands[i].cmd)) {
+ switch (i) {
+ case INTER_SND:
+ if (snd_only) {
+ if (*p < '0' || *p > '9') {
+ goto err_input;
+ }
+ snd_func(p);
+ } else {
+ goto err_input;
+ }
+ break;
+ case INTER_RCV:
+ if (snd_only) {
+ goto err_input;
+ }
+ break;
+ case INTER_SNDBUF:
+ if (set) {
+ if (*p < '0' || *p > '9') {
+ goto err_input;
+ }
+ }
+ len = (set) ? atoi(p) : 0;
+ sndbuf_func(argv0, inter_sk, len, set);
+ break;
+ case INTER_RCVBUF:
+ if (set) {
+ if (*p < '0' || *p > '9') {
+ goto err_input;
+ }
+ }
+ len = (set) ? atoi(p) : 0;
+ rcvbuf_func(argv0, inter_sk, len, set);
+ break;
+ case INTER_BINDX_ADD:
+ tmp_addrs = get_bindx_addr(p, &len);
+ bindx_func(argv0, inter_sk, tmp_addrs, len,
+ SCTP_BINDX_ADD_ADDR, local_port);
+ free(tmp_addrs);
+ break;
+ case INTER_BINDX_REM:
+ tmp_addrs = get_bindx_addr(p, &len);
+ bindx_func(argv0, inter_sk, tmp_addrs, len,
+ SCTP_BINDX_REM_ADDR, local_port);
+ free(tmp_addrs);
+ break;
+ case INTER_SET_PRIM:
+ primary_func(argv0, inter_sk, p, set);
+ break;
+ case INTER_SET_PEER_PRIM:
+ peer_primary_func(argv0, inter_sk, p, set);
+ break;
+ case INTER_HEARTBEAT:
+ spp_hb_demand_func(argv0, inter_sk, p, set);
+ break;
+ case INTER_SHUTDOWN:
+ shutdown_func(argv0, &inter_sk, SHUTDOWN_SHUTDOWN);
+ break;
+ case INTER_ABORT:
+ shutdown_func(argv0, &inter_sk, SHUTDOWN_ABORT);
+ break;
+ case INTER_NODELAY:
+ if (set) {
+ if (*p < '0' || *p > '9') {
+ goto err_input;
+ }
+ }
+ val = (set) ? atoi(p) : 0;
+ nodelay_func(argv0, inter_sk, val, set);
+ break;
+ case INTER_MAXSEG:
+ if (set) {
+ if (*p < '0' || *p > '9') {
+ goto err_input;
+ }
+ }
+ val = (set) ? atoi(p) : 0;
+ maxseg_func(argv0, inter_sk, val, set);
+ break;
+ case INTER_GET_STATS:
+ get_assocstats_func(inter_sk, associd);
+ break;
+ default:
+ goto err_input;
+ break;
+ }
+
+ return i;
+ }
+ i++;
+ }
+
+err_input:
+ printf("Invalid input.\n");
+ return -1;
+
+} /* parse_inter_commands() */
+
+static char *
+gen_message(int len)
+{
+
+ char *buf;
+ char *p;
+ int i;
+
+ buf = malloc(len);
+
+ if (NULL != buf) {
+ for (i = 0, p = buf; i < len; i++, p++) {
+ if (gen_data > GEN_DATA_LAST) {
+ gen_data = GEN_DATA_FIRST;
+ }
+ *p = gen_data++;
+ }
+ }
+
+ return(buf);
+
+} /* gen_message() */
+
+static void
+snd_func(char *input)
+{
+
+ int len;
+
+ len = atoi(input);
+ if (!(inter_outbuf = gen_message(len))) {
+ fprintf(stderr, "Cannot allocate out message.\n");
+ exit(1);
+ }
+ inter_outlen = len;
+
+} /* snd_func() */
+
+static void
+sndbuf_func(char *argv0, int sk, int len, int set)
+{
+ int error;
+ socklen_t optlen;
+
+ if (set) {
+ error = setsockopt(sk, SOL_SOCKET, SO_SNDBUF,
+ (char *)&len, sizeof(len));
+ } else {
+ optlen = sizeof(len);
+ error = getsockopt(sk, SOL_SOCKET, SO_SNDBUF,
+ (char *)&len, &optlen);
+ }
+ if (error != 0) {
+ fprintf(stderr, "%s: Error setting/getting sndbuf: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+
+ if (!set) {
+ printf("sndbuf is %d.\n", len);
+ }
+
+} /* sndbuf_func() */
+
+static void
+rcvbuf_func(char *argv0, int sk, int len, int set)
+{
+ int error;
+ socklen_t optlen;
+
+ if (set) {
+ error = setsockopt(sk, SOL_SOCKET, SO_RCVBUF,
+ (char *)&len, sizeof(len));
+ } else {
+ optlen = sizeof(len);
+ error = getsockopt(sk, SOL_SOCKET, SO_RCVBUF,
+ (char *)&len, &optlen);
+ }
+ if (error != 0) {
+ fprintf(stderr, "%s: Error setting/getting rcvbuf: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+
+ if (!set) {
+ printf("rcvbuf is %d.\n", len);
+ }
+
+} /* rcvbuf_func() */
+
+
+static struct sockaddr *
+get_bindx_addr(char *in, int *count)
+{
+
+ struct sockaddr *tmp_addrs = NULL;
+ char *p = in;
+
+ /* Set the buffer for address parsing. */
+ while ('\n' != *p) {
+ p++;
+ }
+ *p = '\0';
+
+ *count = 0;
+
+ tmp_addrs = append_addr(in, tmp_addrs, count);
+ if (NULL == tmp_addrs) {
+ /* We have no memory, so keep fprintf()
+ * from trying to allocate more.
+ */
+ fprintf(stderr, "No memory to add ");
+ fprintf(stderr, "%s\n", in);
+ exit(2);
+ }
+ return tmp_addrs;
+
+} /* get_bindx_addr() */
+
+static int
+bindx_func(char *argv0, int sk, struct sockaddr *addrs, int count, int flag, int portnum)
+{
+
+ int error;
+ int i;
+ struct sockaddr *sa_addr;
+ void *aptr;
+
+
+ if (0 == portnum) {
+ fprintf(stderr, "%s: A non-0 local port number is ", argv0);
+ fprintf(stderr, "required for bindx to work!\n");
+ return -1 ;
+ }
+
+ /* 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(portnum);
+ aptr += sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)sa_addr)->sin6_port =
+ htons(portnum);
+ aptr += sizeof(struct sockaddr_in6);
+ break;
+ default:
+ fprintf(stderr, "Invalid address family\n");
+ return -1;
+ }
+ }
+
+ error = sctp_bindx(sk, addrs, count, flag);
+
+ if (error != 0) {
+ if (flag == SCTP_BINDX_ADD_ADDR) {
+ fprintf(stderr, "%s: error adding addrs: %s.\n",
+ argv0, strerror(errno));
+ return -1;
+ } else {
+ fprintf(stderr, "%s: error removing addrs: %s.\n",
+ argv0, strerror(errno));
+ return -1;
+ }
+ }
+
+ return 0;
+
+} /* bindx_func() */
+
+static int
+connectx_func(char *argv0, int sk, struct sockaddr *addrs, int count)
+{
+
+ int error;
+ int i;
+ struct sockaddr *sa_addr;
+ void *aptr;
+
+
+ if (0 == remote_port) {
+ fprintf(stderr, "%s: A non-0 remote port number is ", argv0);
+ fprintf(stderr, "required for connectx to work!\n");
+ return -1 ;
+ }
+
+ /* 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");
+ return -1;
+ }
+ }
+
+ error = sctp_connectx(sk, addrs, count, NULL);
+
+ if (error != 0) {
+ if (errno == ECONNREFUSED)
+ return -2;
+ fprintf(stderr, "%s: error connecting to addrs: %s.\n",
+ argv0, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+
+} /* connectx_func() */
+
+static void
+primary_func(char *argv0, int sk, char *cp, int set)
+{
+ struct sctp_prim prim;
+ struct sockaddr_in *in_addr;
+ struct sockaddr_in6 *in6_addr;
+ struct sockaddr *saddr;
+ socklen_t prim_len;
+ int ret;
+ char *p = cp;
+ char addr_buf[INET6_ADDRSTRLEN];
+ const char *ap = NULL;
+
+ prim_len = sizeof(struct sctp_prim);
+ if (!set) {
+ prim.ssp_assoc_id = associd;
+ ret = getsockopt(sk, IPPROTO_SCTP, SCTP_PRIMARY_ADDR,
+ &prim, &prim_len);
+ if (ret < 0)
+ goto err;
+
+ saddr = (struct sockaddr *)&prim.ssp_addr;
+ if (AF_INET == saddr->sa_family) {
+ in_addr = (struct sockaddr_in *)&prim.ssp_addr;
+ ap = inet_ntop(AF_INET, &in_addr->sin_addr, addr_buf,
+ INET6_ADDRSTRLEN);
+ } else if (AF_INET6 == saddr->sa_family) {
+ in6_addr = (struct sockaddr_in6 *)&prim.ssp_addr;
+ ap = inet_ntop(AF_INET6, &in6_addr->sin6_addr, addr_buf,
+ INET6_ADDRSTRLEN);
+ }
+ if (!ap)
+ goto err;
+ printf("%s\n", ap);
+ return;
+ }
+
+ /* Set the buffer for address parsing. */
+ while ('\n' != *p)
+ p++;
+ *p = '\0';
+
+ prim.ssp_assoc_id = associd;
+ if (strchr(cp, '.')) {
+ in_addr = (struct sockaddr_in *)&prim.ssp_addr;
+ in_addr->sin_port = htons(remote_port);
+ in_addr->sin_family = AF_INET;
+ ret = inet_pton (AF_INET, cp, &in_addr->sin_addr);
+ if (ret <= 0)
+ goto err;
+ } else if (strchr(cp, ':')) {
+ in6_addr = (struct sockaddr_in6 *)&prim.ssp_addr;
+ in6_addr->sin6_port = htons(remote_port);
+ in6_addr->sin6_family = AF_INET6;
+ ret = inet_pton(AF_INET6, cp, &in6_addr->sin6_addr);
+ if (ret <= 0)
+ goto err;
+ } else
+ goto err;
+
+ ret = setsockopt(sk, IPPROTO_SCTP, SCTP_PRIMARY_ADDR,
+ &prim, sizeof(struct sctp_prim));
+ if (ret < 0)
+ goto err;
+
+ return;
+err:
+ if (!errno)
+ errno = EINVAL;
+ fprintf(stderr, "%s: error %s primary: %s.\n", argv0,
+ (set)?"setting":"getting", strerror(errno));
+}
+
+static void
+peer_primary_func(char *argv0, int sk, char *cp, int set)
+{
+ struct sctp_setpeerprim setpeerprim;
+ struct sockaddr_in *in_addr;
+ struct sockaddr_in6 *in6_addr;
+ int ret;
+ char *p = cp;
+
+ if (!set) {
+ goto err;
+ }
+
+ /* Set the buffer for address parsing. */
+ while ('\n' != *p)
+ p++;
+ *p = '\0';
+
+ setpeerprim.sspp_assoc_id = associd;
+ if (strchr(cp, '.')) {
+ in_addr = (struct sockaddr_in *)&setpeerprim.sspp_addr;
+ in_addr->sin_port = htons(local_port);
+ in_addr->sin_family = AF_INET;
+ ret = inet_pton (AF_INET, cp, &in_addr->sin_addr);
+ if (ret <= 0)
+ goto err;
+ } else if (strchr(cp, ':')) {
+ in6_addr = (struct sockaddr_in6 *)&setpeerprim.sspp_addr;
+ in6_addr->sin6_port = htons(local_port);
+ in6_addr->sin6_family = AF_INET6;
+ ret = inet_pton(AF_INET6, cp, &in6_addr->sin6_addr);
+ if (ret <= 0)
+ goto err;
+ } else
+ goto err;
+
+ ret = setsockopt(sk, IPPROTO_SCTP, SCTP_SET_PEER_PRIMARY_ADDR,
+ &setpeerprim, sizeof(struct sctp_setpeerprim));
+ if (ret < 0)
+ goto err;
+
+ return;
+err:
+ if (!errno)
+ errno = EINVAL;
+ fprintf(stderr, "%s: error %s peer_primary: %s.\n", argv0,
+ (set)?"setting":"getting", strerror(errno));
+}
+
+static void
+spp_hb_demand_func(char *argv0, int sk, char *cp, int set)
+{
+ struct sctp_paddrparams params;
+ struct sockaddr_in *in_addr;
+ struct sockaddr_in6 *in6_addr;
+ int ret;
+ char *p = cp;
+
+ memset(¶ms, 0, sizeof(struct sctp_paddrparams));
+ params.spp_assoc_id = associd;
+ params.spp_flags = SPP_HB_DEMAND;
+
+ if (set) {
+ /* Set the buffer for address parsing. */
+ while ('\n' != *p)
+ p++;
+ *p = '\0';
+
+ if (strchr(cp, '.')) {
+ in_addr = (struct sockaddr_in *)¶ms.spp_address;
+ in_addr->sin_port = htons(remote_port);
+ in_addr->sin_family = AF_INET;
+ ret = inet_pton(AF_INET, cp, &in_addr->sin_addr);
+ if (ret <= 0)
+ goto err;
+ } else if (strchr(cp, ':')) {
+ in6_addr = (struct sockaddr_in6 *)¶ms.spp_address;
+ in6_addr->sin6_port = htons(remote_port);
+ in6_addr->sin6_family = AF_INET6;
+ ret = inet_pton(AF_INET6, cp, &in6_addr->sin6_addr);
+ if (ret <= 0)
+ goto err;
+ } else
+ goto err;
+ }
+
+ ret = setsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS,
+ ¶ms, sizeof(struct sctp_paddrparams));
+ if (ret < 0)
+ goto err;
+
+ return;
+err:
+ if (!errno)
+ errno = EINVAL;
+ fprintf(stderr, "%s: error %s peer_addr_params: %s.\n", argv0,
+ (set)?"setting":"getting", strerror(errno));
+}
+
+static int
+nodelay_func(char *argv0, int sk, int val, int set)
+{
+ socklen_t optlen;
+ int error;
+
+ if (set) {
+ error = setsockopt(sk, SOL_SCTP, SCTP_NODELAY,
+ (char *)&val, sizeof(val));
+ } else {
+ optlen = sizeof(val);
+ error = getsockopt(sk, SOL_SCTP, SCTP_NODELAY,
+ (char *)&val, &optlen);
+ }
+ if (error != 0) {
+ fprintf(stderr, "%s: Error setting/getting nodelay: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+
+ if (!set) {
+ printf("nodelay is %d.\n", val);
+ }
+
+ return error;
+}
+
+static int
+maxseg_func(char *argv0, int sk, int val, int set)
+{
+ socklen_t optlen;
+ int error;
+
+ if (set) {
+ error = setsockopt(sk, SOL_SCTP, SCTP_MAXSEG,
+ (char *)&val, sizeof(val));
+ } else {
+ optlen = sizeof(val);
+ error = getsockopt(sk, SOL_SCTP, SCTP_MAXSEG,
+ (char *)&val, &optlen);
+ }
+ if (error != 0) {
+ fprintf(stderr, "%s: Error setting/getting maxseg: %s.\n",
+ argv0, strerror(errno));
+ exit(1);
+ }
+
+ if (!set) {
+ printf("maxseg is %d.\n", val);
+ }
+
+ return error;
+}
+
+static int
+shutdown_func(char *argv0, int *skp, int shutdown_type)
+{
+ struct msghdr outmessage;
+ char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+ struct cmsghdr *cmsg;
+ int error=0, bytes_sent;
+ struct sctp_sndrcvinfo *sinfo;
+ struct hostent *hst;
+ char *sd_type;
+ int sk = *skp;
+
+ if (shutdown_type == SHUTDOWN_ABORT)
+ sd_type = "ABORT";
+ else
+ sd_type = "SHUTDOWN";
+
+ /* Verify that the association is present. */
+ error = test_sk_for_assoc(sk, associd);
+ if (error != 0) {
+ printf("The association isn't present yet! Cannot %s!\n", sd_type);
+ return -1;
+ }
+
+ if (socket_type == SOCK_SEQPACKET) {
+ /* Set up the destination. */
+ if (remote_host) {
+ hst = gethostbyname(remote_host);
+ if (hst == NULL) {
+ hst = gethostbyname2(remote_host, AF_INET6);
+ }
+
+ if (hst == NULL || hst->h_length < 1) {
+ fprintf(stderr, "%s: bad hostname: %s\n",
+ argv0, remote_host);
+ exit(1);
+ }
+
+ ra_family = hst->h_addrtype;
+ switch (ra_family) {
+ case AF_INET:
+ ra_len = sizeof(remote_addr.v4);
+ ra_raw = &remote_addr.v4.sin_addr;
+ remote_addr.v4.sin_port = htons(remote_port);
+ remote_addr.v4.sin_family = AF_INET;
+ break;
+ case AF_INET6:
+ ra_len = sizeof(remote_addr.v6);
+ ra_raw = &remote_addr.v6.sin6_addr;
+ remote_addr.v6.sin6_port = htons(remote_port);
+ remote_addr.v6.sin6_family = AF_INET6;
+ break;
+ default:
+ fprintf(stderr, "Invalid address type.\n");
+ exit(1);
+ break;
+ }
+ memcpy(ra_raw, hst->h_addr_list[0], hst->h_length);
+ }
+
+ /* Initialize the message struct we use to pass messages to
+ * the remote socket.
+ */
+ outmessage.msg_name = &remote_addr;
+ outmessage.msg_namelen = ra_len;
+
+ outmessage.msg_iov = NULL;
+ outmessage.msg_iovlen = 0;
+ outmessage.msg_control = outcmsg;
+ outmessage.msg_controllen = sizeof(outcmsg);
+ outmessage.msg_flags = 0;
+
+ cmsg = CMSG_FIRSTHDR(&outmessage);
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+ outmessage.msg_controllen = cmsg->cmsg_len;
+ sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
+ if (shutdown_type == SHUTDOWN_ABORT)
+ sinfo->sinfo_flags |= SCTP_ABORT;
+ else
+ sinfo->sinfo_flags |= SCTP_EOF;
+
+ sinfo->sinfo_assoc_id = associd;
+
+ bytes_sent = sendmsg(sk, &outmessage, 0);
+ if (bytes_sent != 0) {
+ printf("Failure: %s.\n", strerror(errno));
+ return -1;
+ }
+
+ /* Receive the COMM_LOST or SHUTDOWN_COMP event. */
+ test_recv_assoc_change(sk);
+ } else {
+ if (shutdown_type == SHUTDOWN_ABORT) {
+ struct linger {
+ int l_onoff;
+ int l_linger;
+ } data = {1, 0};
+ error = setsockopt(sk, SOL_SOCKET, SO_LINGER,
+ (char *)&data, sizeof(data));
+ if (error != 0) {
+ printf("setsockopt failed %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ error = close(sk);
+ if (error != 0) {
+ printf("close failed %s\n", strerror(errno));
+ exit(1);
+ }
+ *skp = sk = build_endpoint(argv0, local_port);
+ }
+
+ /* Verify that the association is no longer present. */
+ error = test_sk_for_assoc(sk, associd);
+ if (error != 0) {
+ printf("Successfully %s the original association\n", sd_type);
+ associd = 0;
+ new_connection = 1;
+ } else {
+ printf("%s failed\n", sd_type);
+ exit(1);
+ }
+
+ return 0;
+}
+
+static int
+get_assocstats_func(int sk, sctp_assoc_t assoc_id)
+{
+ int error = 0;
+ struct sctp_assoc_stats stats;
+ socklen_t len;
+
+ if (assoc_id == 0) {
+ printf("No association present yet\n");
+ return -1;
+ }
+
+ memset(&stats, 0, sizeof(struct sctp_assoc_stats));
+ stats.sas_assoc_id = assoc_id;
+ len = sizeof(struct sctp_assoc_stats);
+ error = getsockopt(sk, SOL_SCTP, SCTP_GET_ASSOC_STATS,
+ (char *)&stats, &len);
+ if (error != 0) {
+ printf("get_assoc_stats() failed %s\n", strerror(errno));
+ return error;
+ }
+
+ printf("Retransmitted Chunks: %" PRIu64 "\n", (uint64_t) stats.sas_rtxchunks);
+ printf("Gap Acknowledgements Received: %" PRIu64 "\n", (uint64_t) stats.sas_gapcnt);
+ printf("TSN received > next expected: %" PRIu64 "\n", (uint64_t) stats.sas_outofseqtsns);
+ printf("SACKs sent: %" PRIu64 "\n", (uint64_t) stats.sas_osacks);
+ printf("SACKs received: %" PRIu64 "\n", (uint64_t) stats.sas_isacks);
+ printf("Control chunks sent: %" PRIu64 "\n", (uint64_t) stats.sas_octrlchunks);
+ printf("Control chunks received: %" PRIu64 "\n", (uint64_t) stats.sas_ictrlchunks);
+ printf("Ordered data chunks sent: %" PRIu64 "\n", (uint64_t) stats.sas_oodchunks);
+ printf("Ordered data chunks received: %" PRIu64 "\n", (uint64_t) stats.sas_iodchunks);
+ printf("Unordered data chunks sent: %" PRIu64 "\n", (uint64_t) stats.sas_ouodchunks);
+ printf("Unordered data chunks received: %" PRIu64 "\n", (uint64_t) stats.sas_iuodchunks);
+ printf("Dups received (ordered+unordered): %" PRIu64 "\n", (uint64_t) stats.sas_idupchunks);
+ printf("Packets sent: %" PRIu64 "\n", (uint64_t) stats.sas_opackets);
+ printf("Packets received: %" PRIu64 "\n", (uint64_t) stats.sas_ipackets);
+ printf("Maximum Observed RTO this period: %" PRIu64 " - Transport: ", (uint64_t) stats.sas_maxrto);
+ print_sockaddr((struct sockaddr *)&stats.sas_obs_rto_ipaddr);
+ printf("\n");
+
+ return 0;
+}
+
+static int
+test_sk_for_assoc(int sk, sctp_assoc_t assoc_id)
+{
+ int error = 0;
+ struct sctp_status status;
+ socklen_t status_len;
+
+ memset(&status, 0, sizeof(status));
+ if (assoc_id)
+ status.sstat_assoc_id = assoc_id;
+ status_len = sizeof(struct sctp_status);
+ error = getsockopt(sk, SOL_SCTP, SCTP_STATUS,
+ (char *)&status, &status_len);
+ return error;
+}
+
+/* Receive a notification and return the corresponding associd if the event is
+ * SCTP_COMM_UP. Return 0 for any other event.
+ */
+static sctp_assoc_t
+test_recv_assoc_change(int sk)
+{
+ struct msghdr inmessage;
+ struct iovec iov;
+ char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
+ int error;
+
+ /* Initialize inmessage with enough space for DATA... */
+ memset(&inmessage, 0, sizeof(inmessage));
+ if ((iov.iov_base = malloc(REALLY_BIG)) == NULL) {
+ printf("%s: Can't allocate memory.\n", __FUNCTION__);
+ 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);
+
+ error = recvmsg(sk, &inmessage, MSG_WAITALL);
+ if (error < 0) {
+ printf("%s: recvmsg: %s\n", __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+
+ return test_verify_assoc_change(&inmessage);
+}
+
+/* Verify a notification and return the corresponding associd if the event is
+ * SCTP_COMM_UP. Return 0 for any other event.
+ */
+static sctp_assoc_t
+test_verify_assoc_change(struct msghdr *msg)
+{
+ union sctp_notification *sn;
+
+ if (!(msg->msg_flags & MSG_NOTIFICATION)) {
+ fprintf(stderr, "%s: Received data when notification is expected\n",
+ __FUNCTION__);
+ exit(1);
+ }
+
+ sn = (union sctp_notification *)msg->msg_iov->iov_base;
+ if (SCTP_ASSOC_CHANGE != sn->sn_header.sn_type) {
+ fprintf(stderr, "%s: Received unexpected notification: %d",
+ __FUNCTION__, sn->sn_header.sn_type);
+ exit(1);
+ }
+
+ switch(sn->sn_assoc_change.sac_state)
+ {
+ case SCTP_COMM_UP:
+ printf("Received SCTP_COMM_UP\n");
+ break;
+ case SCTP_COMM_LOST:
+ printf("Received SCTP_COMM_LOST\n");
+ break;
+ case SCTP_RESTART:
+ printf("Received SCTP_RESTART\n");
+ break;
+ case SCTP_SHUTDOWN_COMP:
+ printf("Received SCTP_SHUTDOWN_COMP\n");
+ break;
+ case SCTP_CANT_STR_ASSOC:
+ printf("Received SCTP_CANT_STR_ASSOC\n");
+ break;
+ }
+
+ if (SCTP_COMM_UP == sn->sn_assoc_change.sac_state)
+ return sn->sn_assoc_change.sac_assoc_id;
+ else
+ return 0;
+}
+
+void print_addr_buf(void * laddrs, int n_laddrs)
+{
+ void *addr_buf = laddrs;
+ int i;
+
+ for (i = 0; i < n_laddrs; i++) {
+ addr_buf += print_sockaddr((struct sockaddr *)addr_buf);
+ printf("\n");
+ }
+}
+
+int print_sockaddr(struct sockaddr *sa_addr)
+{
+ struct sockaddr_in *in_addr;
+ struct sockaddr_in6 *in6_addr;
+
+ if (AF_INET == sa_addr->sa_family) {
+ in_addr = (struct sockaddr_in *)sa_addr;
+ printf("%d.%d.%d.%d:%d",
+ NIPQUAD(in_addr->sin_addr),
+ ntohs(in_addr->sin_port));
+ return sizeof(struct sockaddr_in);
+ } else {
+ in6_addr = (struct sockaddr_in6 *)sa_addr;
+ printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%d",
+ NIP6(in6_addr->sin6_addr),
+ ntohs(in6_addr->sin6_port));
+ return sizeof(struct sockaddr_in6);
+ }
+}
diff --git a/src/apps/sctp_darn.h b/src/apps/sctp_darn.h
new file mode 100644
index 0000000..e2c1ef1
--- /dev/null
+++ b/src/apps/sctp_darn.h
@@ -0,0 +1,29 @@
+#ifndef __sctp_darn_h__
+#define __sctp_darn_h__
+
+#define REALLY_BIG 65536
+#define SCTP_TESTPORT_1 1
+#define SCTP_TESTPORT_2 2
+
+void parse_arguments(int argc, char *argv[]);
+void usage(char *argv0);
+int command_listen(char *arg0, int sk);
+int command_send(char *arg0, int *skp);
+int command_poll(char *arg0);
+int test_print_message(int sk, struct msghdr *, size_t msg_len);
+
+typedef enum {
+ COMMAND_NONE = 0,
+ COMMAND_LISTEN,
+ COMMAND_SEND,
+ COMMAND_POLL,
+} command_t;
+
+typedef union {
+ struct sockaddr_storage ss;
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ struct sockaddr sa;
+} sockaddr_storage_t;
+
+#endif /* __sctp_darn_h__ */
diff --git a/src/apps/sctp_status.c b/src/apps/sctp_status.c
new file mode 100644
index 0000000..8563cbe6
--- /dev/null
+++ b/src/apps/sctp_status.c
@@ -0,0 +1,945 @@
+/* SCTP kernel reference Implementation
+ * (C) Copyright Fujitsu Ltd. 2008, 2009
+ *
+ * The SCTP reference implementation 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.
+ *
+ * The SCTP reference implementation 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
+ *
+ * Any bugs reported to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ *
+ * Written or modified by:
+ * Wei Yongjun <yjwei@cn.fujitsu.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/sctp.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+
+#define DEFAULT_SEC 0
+#define DEFAULT_USEC 5000
+
+#define REALLY_BIG 65536
+
+#define SERVER 0
+#define CLIENT 1
+#define NOT_DEFINED 666
+
+#define DEBUG_NONE 0
+#define DEBUG_MIN 1
+#define DEBUG_MAX 2
+
+#define ORDER_PATTERN_UNORDERED 0
+#define ORDER_PATTERN_ORDERED 1
+#define ORDER_PATTERN_ALTERNATE 2
+#define ORDER_PATTERN_RANDOM 3
+
+#define STREAM_PATTERN_SEQUENTIAL 0
+#define STREAM_PATTERN_RANDOM 1
+
+#define MAX_BIND_RETRYS 10
+#define BIG_REPEAT 1000000
+#define REPEAT 10
+
+#define DEFAULT_MAX_WINDOW 32768
+#define DEFAULT_MIN_WINDOW 1500
+
+#define MSG_CNT 10
+
+#define DEBUG_PRINT(level, print_this...) \
+{ \
+ if (debug_level >= level) { \
+ fprintf(stdout, print_this); \
+ fflush(stdout); \
+ } \
+} /* DEBUG_PRINT */
+
+char *local_host = NULL;
+int local_port = 0;
+char *remote_host = NULL;
+int remote_port = 0;
+struct sockaddr_storage s_rem, s_loc;
+int r_len, l_len;
+int size_arg = 0;
+int debug_level = DEBUG_NONE;
+int order_pattern = ORDER_PATTERN_UNORDERED;
+int order_state = 0;
+int stream_pattern = STREAM_PATTERN_SEQUENTIAL;
+int stream_state = 0;
+int repeat = REPEAT;
+int repeat_count = 0;
+int max_msgsize = DEFAULT_MAX_WINDOW;
+int msg_cnt = MSG_CNT;
+int drain = 0;
+int max_stream = 0;
+int gsk = -1;
+int period = 1;
+char *statusfile = NULL;
+
+void printstatus(int sk);
+void sighandler(int signo);
+void settimerhandle(void);
+void usage(char *argv0);
+void start_test(int role);
+
+unsigned char msg[] = "012345678901234567890123456789012345678901234567890";
+
+/* Convenience structure to determine space needed for cmsg. */
+typedef union {
+ struct sctp_initmsg init;
+ struct sctp_sndrcvinfo sndrcvinfo;
+} _sctp_cmsg_data_t;
+
+int main(int argc, char *argv[]) {
+ int c, role = NOT_DEFINED;
+ char *interface = NULL;
+ struct sockaddr_in *t_addr;
+ struct sockaddr_in6 *t_addr6;
+
+ /* Parse the arguments. */
+ while ((c = getopt(argc, argv, ":H:L:P:h:p:c:d:lm:sx:X:o:M:Di:I:f:")) >= 0 ) {
+ switch (c) {
+ case 'H':
+ local_host = optarg;
+ break;
+ case 'P':
+ local_port = atoi(optarg);
+ break;
+ case 'h':
+ remote_host = optarg;
+ break;
+ case 'p':
+ remote_port = atoi(optarg);
+ 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 's':
+ if (role != NOT_DEFINED) {
+ printf("%s: only -s or -l\n", argv[0]);
+ usage(argv[0]);
+ exit(1);
+ }
+ role = CLIENT;
+ break;
+ case 'D':
+ drain = 1;
+ break;
+ case 'd':
+ debug_level = atoi(optarg);
+ if (debug_level < DEBUG_NONE
+ || debug_level > DEBUG_MAX) {
+ usage(argv[0]);
+ exit(1);
+ }
+ break;
+ case 'I':
+ period = atoi(optarg);
+ if (period < 0) {
+ usage(argv[0]);
+ exit(1);
+ }
+ break;
+ case 'x':
+ repeat = atoi(optarg);
+ if (!repeat) {
+ 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':
+ size_arg = atoi(optarg);
+ if (size_arg < 0) {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ 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 'M':
+ max_stream = atoi(optarg);
+ if (max_stream < 0
+ || max_stream >= (1<<16)) {
+ usage(argv[0]);
+ exit(1);
+ }
+ break;
+ case 'm':
+ max_msgsize = atoi(optarg);
+ break;
+ case 'i':
+ interface = optarg;
+ break;
+ case 'f':
+ statusfile = optarg;
+ 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) {
+ fprintf(stderr, "%s: Client needs at least remote address "
+ "& port\n", argv[0]);
+ 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 && 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 (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);
+ }
+
+ /* Let the testing begin. */
+ start_test(role);
+
+ return 0;
+}
+
+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 ) {
+ fprintf(stderr, "\n\n\t\t***bind: can "
+ "not bind to %s:%s: %s ****\n",
+ host_s, serv_s, strerror(errno));
+ exit(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);
+
+ return 0;
+} /* bind_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, 1);
+ if (error != 0) {
+ fprintf(stderr, "\n\n\t\t*** listen: %s ***\n\n\n", strerror(errno));
+ exit(1);
+ }
+
+ return 0;
+} /* listen_r() */
+
+int accept_r(int sk){
+ socklen_t len = 0;
+
+ DEBUG_PRINT(DEBUG_MIN, "\taccept(sk=%d)\n", sk);
+
+ gsk = accept(sk, NULL, &len);
+ if (gsk < 0) {
+ fprintf(stderr, "\n\n\t\t*** accept: %s ***\n\n\n", strerror(errno));
+ exit(1);
+ }
+
+ return 0;
+} /* 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) {
+ fprintf(stderr, "\n\n\t\t*** connect: %s ***\n\n\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ gsk = sk;
+
+ return 0;
+} /* connect_r() */
+
+int close_r(int sk) {
+ int error = 0;
+
+ DEBUG_PRINT(DEBUG_MIN, "\tclose(sk=%d)\n",sk);
+
+ error = close(sk);
+ if (error != 0) {
+ fprintf(stderr, "\n\n\t\t*** close: %s ***\n\n",
+ strerror(errno));
+ exit(1);
+ }
+ fflush(stdout);
+ return 0;
+} /* close_r() */
+
+int receive_r(int sk)
+{
+ int 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) {
+ DEBUG_PRINT(DEBUG_MIN, "\trecvmsg(sk=%d) ", sk);
+
+ error = recvmsg(sk, &inmessage, MSG_WAITALL);
+ if (error < 0 && errno != EAGAIN) {
+ fprintf(stderr, "\n\t\t*** recvmsg: %s ***\n\n",
+ strerror(errno));
+ fflush(stdout);
+ close(sk);
+ free(iov.iov_base);
+ exit(1);
+ } else if (error == 0) {
+ printf("\n\t\trecvmsg() returned 0 !!!!\n");
+ fflush(stdout);
+ }
+
+ if(MSG_NOTIFICATION & inmessage.msg_flags)
+ continue; /* got a notification... */
+
+ inmessage.msg_control = incmsg;
+ inmessage.msg_controllen = sizeof(incmsg);
+ iov.iov_len = REALLY_BIG;
+ break;
+ }
+
+ free(iov.iov_base);
+ return 0;
+} /* receive_r () */
+
+void server(int sk) {
+ int i;
+
+ if (max_msgsize > DEFAULT_MAX_WINDOW) {
+ if (setsockopt(sk, IPPROTO_SCTP, SO_RCVBUF, &max_msgsize,
+ sizeof(max_msgsize)) < 0) {
+ perror("setsockopt(SO_RCVBUF)");
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < msg_cnt; i++) {
+ receive_r(sk);
+ DEBUG_PRINT(DEBUG_MIN, "count %d\n", i+1);
+ }
+} /* server() */
+
+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() */
+
+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 {
+ exit(1);
+ }
+
+ 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;
+
+ 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);
+ exit(1);
+ }
+
+ if (send_size > 0) free(message);
+ return 0;
+} /* send_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;
+ 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 {
+ msg_size = (rand() % max_msgsize) + 1;
+ }
+
+ return msg_size;
+
+} /* next_msg_size() */
+
+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) {
+ close(sk);
+ break;
+ }
+
+ /* The sender is echoing so do discard the echoed data. */
+ if (drain && ((i + 1) % period == 0)) {
+ receive_r(sk);
+ }
+ }
+} /* client() */
+
+void start_test(int role) {
+ int sk, pid, ret;
+ int i = 0;
+
+ DEBUG_PRINT(DEBUG_NONE, "\nStarting tests...\n");
+
+ repeat_count = repeat;
+
+ DEBUG_PRINT(DEBUG_MIN, "\tsocket(SOCK_STREAM, IPPROTO_SCTP)");
+
+ if ((sk = socket(s_loc.ss_family, SOCK_STREAM, IPPROTO_SCTP)) < 0 ) {
+ fprintf(stderr, "\n\n\t\t*** socket: failed to create"
+ " socket: %s ***\n", strerror(errno));
+ exit(1);
+ }
+ DEBUG_PRINT(DEBUG_MIN, " -> sk=%d\n", sk);
+
+ bind_r(sk, &s_loc);
+
+ if (role == SERVER) {
+ listen_r(sk, 1);
+ accept_r(sk);
+ } else {
+ if (max_stream > 0) {
+ struct sctp_initmsg initmsg;
+
+ memset(&initmsg, 0, sizeof(initmsg));
+ initmsg.sinit_num_ostreams = max_stream;
+ initmsg.sinit_max_instreams = max_stream;
+ initmsg.sinit_max_attempts = 3;
+
+ ret = setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG,
+ &initmsg, sizeof(initmsg));
+ if (ret < 0) {
+ perror("setsockopt(SCTP_INITMSG)");
+ exit(0);
+ }
+ }
+
+ connect_r(sk, (struct sockaddr *)&s_rem, r_len);
+ }
+
+ if ((pid = fork()) == 0) {
+ settimerhandle();
+ printstatus(gsk);
+ while(1);
+ } else {
+ if (!debug_level) {
+ printf(" ");
+ }
+
+ for(i = 0; i < repeat_count; i++) {
+
+ if (role == SERVER) {
+ DEBUG_PRINT(DEBUG_NONE, "Server: Receiving packets.(%d/%d)\n",
+ i+1, repeat_count);
+ server(gsk);
+ } else {
+ DEBUG_PRINT(DEBUG_NONE, "Client: Sending packets.(%d/%d)\n",
+ i+1, repeat_count);
+ client(sk);
+ }
+
+ fflush(stdout);
+ }
+
+ if (role == SERVER) close_r(gsk);
+ close_r(sk);
+ }
+} /* start_test() */
+
+void settimerhandle(void) {
+ struct sigaction act;
+ struct itimerval interval;
+
+ act.sa_handler = sighandler;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGPROF, &act, NULL);
+
+ interval.it_value.tv_sec = DEFAULT_SEC;
+ interval.it_value.tv_usec = DEFAULT_USEC;
+ interval.it_interval = interval.it_value;
+
+ setitimer(ITIMER_PROF, &interval, NULL);
+}
+
+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"
+ "\t [-f status-file]\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] ream-pattern]\n"
+ "\t [-M max-stream]\n"
+ "\t [-m max-msgsize]\n"
+ "\t [-L num-ports] [-S num-ports]\n"
+ "\t [-i interface]\n"
+ "\t [-f status-file]\n",
+ argv0);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "\t-c value = Packets of specifed size.\n");
+ fprintf(stderr, "\t-m msgsize(1500-65515, default value 32768)\n");
+ fprintf(stderr, "\t-x number of repeats\n");
+ fprintf(stderr, "\t-X number of messages\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-M max-stream (default value 0)\n");
+ fprintf(stderr, "\t-D drain. If in client mode do a read following send.\n");
+ fprintf(stderr, "\t-I receive after <n> times of send, default value 1.\n");
+ fprintf(stderr, "\n");
+ fflush(stderr);
+
+} /* usage() */
+
+void sighandler(int signo) {
+ DEBUG_PRINT(DEBUG_MAX, "timeout sig\n");
+ printstatus(gsk);
+}
+
+char* get_sstat_state(int state) {
+ switch(state) {
+ case SCTP_EMPTY:
+ return "EMPTY";
+ case SCTP_CLOSED:
+ return "CLOSED";
+ case SCTP_COOKIE_WAIT:
+ return "COOKIE_WAIT";
+ case SCTP_COOKIE_ECHOED:
+ return "COOKIE_ECHOED";
+ case SCTP_ESTABLISHED:
+ return "ESTABLISHED";
+ case SCTP_SHUTDOWN_PENDING:
+ return "SHUTDOWN_PENDING";
+ case SCTP_SHUTDOWN_SENT:
+ return "SHUTDOWN_SENT";
+ case SCTP_SHUTDOWN_RECEIVED:
+ return "SHUTDOWN_RECEIVED";
+ case SCTP_SHUTDOWN_ACK_SENT:
+ return "SHUTDOWN_ACK_SENT";
+ default:
+ return "UNKNOW";
+ }
+}
+
+void printstatus(int sk) {
+ static int cwnd = 0;
+ static int count = 0;
+ struct sctp_status status;
+ socklen_t optlen;
+ FILE * fp;
+ const char *state_to_str[] = {
+ [SCTP_INACTIVE] = "INACTIVE",
+ [SCTP_PF] = "PF",
+ [SCTP_ACTIVE] = "ACTIVE",
+ [SCTP_UNCONFIRMED] = "UNCONFIRMED",
+ };
+
+ optlen = sizeof(struct sctp_status);
+ if(getsockopt(sk, IPPROTO_SCTP, SCTP_STATUS, &status, &optlen) < 0) {
+ fprintf(stderr, "Error getting status: %s.\n", strerror(errno));
+ exit(1);
+ }
+
+ if (statusfile != NULL) {
+ if (count == 0)
+ unlink(statusfile);
+
+ if((fp = fopen(statusfile, "a+")) == NULL) {
+ perror("fopen");
+ exit(1);
+ }
+ } else
+ fp = stdout;
+
+ if (count == 0)
+ fprintf(fp, "NO. ASSOC-ID STATE RWND UNACKDATA PENDDATA INSTRMS OUTSTRMS "
+ "FRAG-POINT SPINFO-STATE SPINFO-CWDN SPINFO-SRTT SPINFO-RTO SPINFO-MTU\n");
+
+ if (cwnd != status.sstat_primary.spinfo_cwnd) {
+ count++;
+
+ fprintf(fp, "%-3d %-8d %-17s %-8d %-9d %-8d %-7d %-8d %-10d %-12s %-11d %-11d %-10d %d\n", count,
+ status.sstat_assoc_id, get_sstat_state(status.sstat_state),
+ status.sstat_rwnd, status.sstat_unackdata, status.sstat_penddata,
+ status.sstat_instrms, status.sstat_outstrms, status.sstat_fragmentation_point,
+ state_to_str[status.sstat_primary.spinfo_state],
+ status.sstat_primary.spinfo_cwnd, status.sstat_primary.spinfo_srtt,
+ status.sstat_primary.spinfo_rto, status.sstat_primary.spinfo_mtu);
+ }
+
+ cwnd = status.sstat_primary.spinfo_cwnd;
+
+ fflush(fp);
+
+ if (fp != stdout)
+ fclose(fp);
+
+ if (status.sstat_primary.spinfo_state != SCTP_ACTIVE) {
+ close_r(sk);
+ exit(1);
+ }
+}
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() */
diff --git a/src/apps/sctp_xconnect.c b/src/apps/sctp_xconnect.c
new file mode 100644
index 0000000..6759c0e
--- /dev/null
+++ b/src/apps/sctp_xconnect.c
@@ -0,0 +1,579 @@
+/* SCTP kernel Implementation
+ * (C) Copyright IBM Corp. 2003
+ *
+ * The SCTP implementation 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.
+ *
+ * The SCTP implementation 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
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ *
+ * Written or modified by:
+ * Ryan Layer <rmlayer@us.ibm.com>
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.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 <sctputil.h>
+#include <netdb.h>
+#include <getopt.h>
+
+char *TCID = __FILE__;
+int TST_TOTAL = 1;
+int TST_CNT = 0;
+
+#define MAXHOSTNAME 64
+
+#define MAXCLIENTNUM 10000
+
+#define TRUE 1
+
+#define SERVER 1
+#define CLIENT 0
+#define NOT_DEFINED -1
+
+int mode = NOT_DEFINED;
+
+int assoc_num,
+ remote_port,
+ local_port;
+int active = 0;
+
+char *local_host = NULL;
+char *remote_host = NULL;
+sockaddr_storage_t client_loop,
+ server_loop;
+struct hostent *hst;
+char big_buffer[REALLY_BIG];
+
+void usage(char *argv0);
+void parse_arguments(int argc, char*argv[]);
+void data_received(struct msghdr *inmessage, int len, int stream,
+ int server_socket);
+int event_received(struct msghdr *inmessage, int assoc_num);
+void process_ready_sockets(int client_socket[], int assoc_num, fd_set *rfds);
+void server_mode(void);
+void client_mode(void);
+
+/* Print the syntax/usage */
+void usage(char *argv0)
+{
+ printf("usage: %s -H localhost -P localport -l|c [-h remotehost]\n"
+ "\t\t[-p remoteport] [-a] [-n <cnt>]\n"
+ " -H\t\tspecify a local address.\n"
+ " -P\t\tspecify the local port number to be used\n"
+ " -l\t\trun in server mode.\n"
+ " -c\t\trun in client mode.\n"
+ " -h\t\tspecify the peer address.\n"
+ " -p\t\tspecify the port number for the peer address.\n"
+ " -a\t\tactively generate traffic with the server.\n"
+ " -n\t\tspecify the number of associations to create.\n",
+ argv0);
+}
+
+/* Parse command line options */
+void parse_arguments(int argc, char*argv[]) {
+ int c;
+
+ while ((c = getopt(argc, argv, ":H:P:ach:ln:p:")) >= 0) {
+ switch (c) {
+ case 'H':
+ local_host = optarg;
+ break;
+ case 'P':
+ local_port = atoi(optarg);
+ break;
+ case 'c':
+ if (mode == NOT_DEFINED)
+ mode = CLIENT;
+ else {
+ usage(argv[0]);
+ exit(0);
+ }
+ break;
+ case 'a':
+ active = 1;
+ break;
+ case 'h':
+ remote_host = optarg;
+ break;
+ case 'l':
+ if (mode == NOT_DEFINED)
+ mode = SERVER;
+ else {
+ usage(argv[0]);
+ exit(0);
+ }
+ break;
+ case 'n':
+ assoc_num = atoi(optarg);
+ break;
+ case 'p':
+ remote_port = atoi(optarg);
+ break;
+ default:
+ usage(argv[0]);
+ exit(0);
+ }
+ } /* while() */
+
+ if (mode == CLIENT) {
+ if (assoc_num) {
+ if (assoc_num > MAXCLIENTNUM) {
+ printf("The number of associations indicated "
+ "is greater than the");
+ printf("max number of associations "
+ "allowed(%d).", MAXCLIENTNUM);
+ usage(argv[0]);
+ exit(0);
+ }
+ } else
+ assoc_num = 1;
+
+ if (remote_host && remote_port) {
+ hst = gethostbyname(remote_host);
+
+ memcpy(&server_loop.v4.sin_addr, hst->h_addr_list[0],
+ sizeof(server_loop.v4.sin_addr));
+
+ server_loop.v4.sin_family = AF_INET;
+server_loop.v4.sin_port = htons(remote_port);
+ } else {
+ printf("Remote host and remote port must be defined "
+ "in client mode\n");
+ usage(argv[0]);
+ exit(0);
+ }
+
+ if (local_host) {
+ hst = gethostbyname(local_host);
+
+ memcpy(&client_loop.v4.sin_addr, hst->h_addr_list[0],
+ sizeof(client_loop.v4.sin_addr));
+ } else
+ client_loop.v4.sin_addr.s_addr = INADDR_ANY;
+
+ if (local_port)
+ client_loop.v4.sin_port = htons(local_port);
+ else
+ client_loop.v4.sin_port = 0;
+
+ client_loop.v4.sin_family = AF_INET;
+ } else if (mode == SERVER) {
+ if (active) {
+ printf("This option if for client use only");
+ usage(argv[0]);
+ exit(0);
+ }
+
+ if (remote_host || remote_port) {
+ printf("Remote values not needed in server mode.\n");
+ usage(argv[0]);
+ exit(0);
+ }
+
+ if (local_host) {
+ hst = gethostbyname(local_host);
+
+ memcpy(&server_loop.v4.sin_addr, hst->h_addr_list[0],
+ sizeof(server_loop.v4.sin_addr));
+ } else
+ server_loop.v4.sin_addr.s_addr = INADDR_ANY;
+
+ if (local_port)
+ server_loop.v4.sin_port = htons(local_port);
+ else {
+ printf("Specify a local port in server mode.\n");
+ usage(argv[0]);
+ exit(0);
+ }
+
+ server_loop.v4.sin_family = AF_INET;
+ } else {
+ printf("Must assisgn a client or server mode.\n");
+ usage(argv[0]);
+ exit(0);
+ }
+} /* parse_arguments() */
+
+/* Handle data received */
+void data_received(struct msghdr *inmessage, int len, int stream, int socket) {
+
+ int ppid, error;
+ char *ping = "PING";
+
+ if (mode == SERVER) {
+ ppid = rand();
+
+ error = sctp_sendmsg(socket,
+ inmessage->msg_iov->iov_base,
+ len,
+ (struct sockaddr *)inmessage->msg_name,
+ inmessage->msg_namelen,
+ ppid,
+ 0,
+ stream,
+ 0, 0);
+
+ if (error < 0) {
+ printf("Send Failure: %s.\n", strerror(errno));
+ DUMP_CORE;
+ }
+ } else {
+ ppid = rand();
+
+ printf("Data Received by socket #: %d.\n", socket);
+ printf("\tMessage = %s\n",
+ (char *)inmessage->msg_iov->iov_base);
+
+ if (active) {
+ error = sctp_sendmsg(socket, ping, strlen(ping) + 1,
+ (struct sockaddr *)&server_loop,
+ sizeof(server_loop), ppid, 0,
+ stream, 0, 0);
+ if (error < 0) {
+ printf("Send Failure: %s.\n",
+ strerror(errno));
+ DUMP_CORE;
+ }
+ }
+ }
+}
+
+/* This will print what type of SCTP_ASSOC_CHANGE state that was received */
+void print_sctp_sac_state(struct msghdr *msg) {
+
+ char *data;
+ union sctp_notification *sn;
+
+ if (msg->msg_flags & MSG_NOTIFICATION) {
+ data = (char *)msg->msg_iov[0].iov_base;
+
+ sn = (union sctp_notification *)data;
+
+ switch (sn->sn_assoc_change.sac_state) {
+ case SCTP_COMM_UP:
+ printf("SCTP_COMM_UP\n");
+ break;
+ case SCTP_COMM_LOST:
+ printf("SCTP_COMM_LOST\n");
+ break;
+ case SCTP_RESTART:
+ printf("SCTP_RESTART");
+ break;
+ case SCTP_SHUTDOWN_COMP:
+ printf("SCTP_SHUTDOWN_COMP\n");
+ break;
+ case SCTP_CANT_STR_ASSOC:
+ printf("SCTP_CANT_STR_ASSOC\n");
+ break;
+ default:
+ break;
+ }
+ }
+} /* void print_sctp_sac_state() */
+
+/* Tests what type of MSG_NOTIFICATION has been received.
+* For now this fucntion only works with SCTP_ASSOC_CHANGE
+* types, but can be easily expanded.
+*
+* Will return...
+* -1 if the msg_flags is not MSG_NOTIFICATION
+* 0 if the MSG_NOTIFICATION type differs from the type
+* passed into the additional variable
+* 1 if the MSG_NOTIFICATION type matches the type
+* passed into the additional variable
+*/
+int test_check_notification_type(struct msghdr *msg,
+ uint16_t sn_type,
+ uint32_t additional) {
+
+ char *data;
+ union sctp_notification *sn;
+
+ if (!(msg->msg_flags & MSG_NOTIFICATION)) {
+ return -1;
+ } else {
+
+ /* Fixup for testframe. */
+ data = (char *)msg->msg_iov[0].iov_base;
+
+ sn = (union sctp_notification *)data;
+
+ if (sn->sn_header.sn_type != sn_type)
+ return 0;
+ else if (sn->sn_header.sn_type == SCTP_ASSOC_CHANGE)
+ if (sn->sn_assoc_change.sac_state == additional)
+ return 1;
+ return 0;
+ }
+}
+
+/* Determine the type of event and make correct adjustments to the
+* association count
+*/
+int event_received(struct msghdr *inmessage, int assoc_num) {
+
+ int error;
+
+ printf("Event Received\n");
+
+ print_sctp_sac_state(inmessage);
+
+ if (mode == SERVER) {
+ /* Test type of Event */
+ error = test_check_notification_type(inmessage,
+ SCTP_ASSOC_CHANGE,
+ SCTP_COMM_UP);
+ if (error > 0) {
+ assoc_num++;
+ printf("Assosiation Established: count = %d.\n",
+ assoc_num);
+ } else {
+ error = test_check_notification_type(inmessage,
+ SCTP_ASSOC_CHANGE,
+ SCTP_SHUTDOWN_COMP);
+
+ if (error > 0) {
+ assoc_num--;
+ printf("Assosiation Shutdown: count = %d.\n",
+ assoc_num);
+ }
+ }
+ }
+ return assoc_num;
+}
+
+void server_mode() {
+ sockaddr_storage_t msgname;
+ int server_socket,
+ error,
+ stream;
+ int assoc_num =0;
+ struct msghdr inmessage;
+ struct iovec iov;
+ char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))];
+
+
+ printf("Running in Server Mode...\n");
+
+ memset(&inmessage, 0, sizeof(inmessage));
+ iov.iov_base = big_buffer;
+ iov.iov_len = REALLY_BIG;
+ inmessage.msg_iov = &iov;
+ inmessage.msg_iovlen =1;
+ inmessage.msg_control = incmsg;
+ inmessage.msg_controllen = sizeof(incmsg);
+ inmessage.msg_name = &msgname;
+ inmessage.msg_namelen = sizeof (msgname);
+
+ stream = 1;
+
+ server_socket = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
+ if (server_socket < 0) {
+ printf("Socket Failure: %s.\n", strerror(errno));
+ DUMP_CORE;
+ }
+
+ error = bind(server_socket, &server_loop.sa, sizeof(server_loop));
+ if (error != 0 ) {
+ printf("Bind Failure: %s.\n", strerror(errno));
+ DUMP_CORE;
+ }
+
+ error = listen(server_socket, 1);
+ if (error != 0) {
+ printf("Listen Failure: %s.\n", strerror(errno));
+ DUMP_CORE;
+ }
+ while (TRUE) {
+ error = recvmsg(server_socket, &inmessage, MSG_WAITALL);
+ if (error < 0) {
+ printf("Receive Failure: %s\n",
+ strerror(errno));
+ } else {
+ if (inmessage.msg_flags & MSG_NOTIFICATION)
+ assoc_num = event_received(&inmessage, assoc_num);
+ else
+ data_received(&inmessage, error, stream, server_socket);
+ }
+ }
+}
+
+void client_mode() {
+
+ int i, error, stream, max_socket = 0;
+ uint32_t ppid = 0;
+ int client_socket[assoc_num];
+ char *message = "Awake";
+ fd_set rfds;
+ struct timeval tv;
+
+ stream = 1;
+
+ printf("Running in Client Mode...\n");
+
+ /* Create the sockets */
+ for (i = 0; i < assoc_num; i++) {
+ client_socket[i] = socket(PF_INET, SOCK_SEQPACKET,
+ IPPROTO_SCTP);
+ if (client_socket[i] < 0 ){
+ printf("Socket Failure: %s.\n", strerror(errno));
+ DUMP_CORE;
+ }
+
+ if (local_port) {
+ error = bind(client_socket[i], &client_loop.sa,
+ sizeof(client_loop));
+ if (error < 0) {
+ printf("Bind Failure: %s\n", strerror(errno));
+ DUMP_CORE;
+ }
+ }
+
+ printf("Create Socket #: %d\n", client_socket[i]);
+
+ /* Connect to server and send initial message */
+ error = connect(client_socket[i], &server_loop.sa,
+ sizeof(server_loop));
+ if (error < 0){
+ printf("Connect Failure: %s.\n", strerror(errno));
+ DUMP_CORE;
+ }
+
+ max_socket = client_socket[i];
+
+ ppid++;
+
+ /* Send initial message */
+ error = sctp_sendmsg(client_socket[i],
+ message,
+ strlen(message) + 1,
+ (struct sockaddr *)&server_loop,
+ sizeof(server_loop),
+ ppid,
+ 0,
+ stream,
+ 0, 0);
+ if (error < 0 ) {
+ printf("Send Failure: %s.\n", strerror(errno));
+ DUMP_CORE;
+ }
+ }
+
+ while (TRUE){
+
+ /* Clear the set for select() */
+ FD_ZERO(&rfds);
+
+ /* Set time out values for select() */
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ /* Add the sockets select() will examine */
+ for (i = 0; i < assoc_num; i++) {
+ FD_SET(client_socket[i], &rfds);
+ }
+
+ /* Wait until there is data to be read from one of the
+ * sockets, or until the timer expires
+ */
+ error = select(max_socket + 1, &rfds, NULL, NULL, &tv);
+
+ if (error < 0) {
+ printf("Select Failure: %s.\n", strerror(errno));
+ DUMP_CORE;
+ } else if (error) {
+ /* Loop through the array of sockets to find the ones
+ *that have information to be read
+ */
+ process_ready_sockets(client_socket, assoc_num, &rfds);
+ }
+ }
+}
+
+void process_ready_sockets(int client_socket[], int assoc_num, fd_set *rfds) {
+
+ int i, stream, error;
+ struct msghdr inmessage;
+ struct iovec iov;
+ char incmsg[CMSG_SPACE(sizeof (sctp_cmsg_data_t))];
+ sockaddr_storage_t msgname;
+
+ /* Setup inmessage to be able to receive in incomming message */
+ memset(&inmessage, 0, sizeof (inmessage));
+ iov.iov_base = big_buffer;
+ iov.iov_len = REALLY_BIG;
+ inmessage.msg_iov = &iov;
+ inmessage.msg_iovlen =1;
+ inmessage.msg_control = incmsg;
+ inmessage.msg_controllen = sizeof (incmsg);
+ inmessage.msg_name = &msgname;
+ inmessage.msg_namelen = sizeof (msgname);
+
+ stream = 1;
+
+ for( i = 0; i < assoc_num; i++) {
+ if (FD_ISSET(client_socket[i], rfds)) {
+ error = recvmsg(client_socket[i], &inmessage,
+ MSG_WAITALL);
+ if (error < 0)
+ printf("Receive Failure: %s\n",
+ strerror(errno));
+ else {
+ /* Test to find the type of message that was read(event/data) */
+ if (inmessage.msg_flags &
+ MSG_NOTIFICATION)
+ event_received(&inmessage,
+ 0);
+
+ else
+ data_received(&inmessage, error,
+ stream,
+ client_socket[i]);
+ }
+ }
+ }
+}
+
+int main(int argc, char *argv[]) {
+
+ parse_arguments(argc, argv);
+
+ if (mode == SERVER) {
+ server_mode();
+ } else if (mode == CLIENT){
+ client_mode();
+ }
+ exit(1);
+}
+