# HG changeset patch
# User Susant Sahani <ssahani@redhat.com>
# Date 1388413868 -19800
#      Mon Dec 30 20:01:08 2013 +0530
# Node ID 4a1ed3c2c085907567083884ef67f33dbd2af496
# Parent  1536985dc8547e98e43283dd07de68a853a67faa
SCTP Support for Iperf

 Added SCTP support for iperf
 --sctp

diff -r 1536985dc854 -r 4a1ed3c2c085 src/Makefile.am
--- a/src/Makefile.am	Fri Dec 20 15:05:21 2013 -0800
+++ b/src/Makefile.am	Mon Dec 30 20:01:08 2013 +0530
@@ -18,6 +18,8 @@
                         iperf_tcp.h \
                         iperf_udp.c \
                         iperf_udp.h \
+			iperf_sctp.c \
+	                iperf_sctp.h \
                         iperf_util.c \
                         iperf_util.h \
                         locale.c \
diff -r 1536985dc854 -r 4a1ed3c2c085 src/iperf_api.c
--- a/src/iperf_api.c	Fri Dec 20 15:05:21 2013 -0800
+++ b/src/iperf_api.c	Mon Dec 30 20:01:08 2013 +0530
@@ -40,6 +40,7 @@
 #include "iperf_api.h"
 #include "iperf_udp.h"
 #include "iperf_tcp.h"
+#include "iperf_sctp.h"
 #include "timer.h"
 
 #include "cjson.h"
@@ -535,6 +536,7 @@
         {"title", required_argument, NULL, 'T'},
 #if defined(linux)
         {"linux-congestion", required_argument, NULL, 'C'},
+        {"sctp", no_argument, NULL, 'x'},
 #endif
         {"help", no_argument, NULL, 'h'},
         {NULL, 0, NULL, 0}
@@ -547,7 +549,7 @@
 
     blksize = 0;
     server_flag = client_flag = rate_flag = duration_flag = 0;
-    while ((flag = getopt_long(argc, argv, "p:f:i:DVJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:h", longopts, NULL)) != -1) {
+    while ((flag = getopt_long(argc, argv, "p:f:i:DVJvsc:ub:t:n:k:l:x:P:Rw:B:M:N46S:L:ZO:F:A:T:C:h", longopts, NULL)) != -1) {
         switch (flag) {
             case 'p':
                 test->server_port = atoi(optarg);
@@ -597,6 +599,16 @@
                 set_protocol(test, Pudp);
 		client_flag = 1;
                 break;
+            case 'x':
+#if defined(linux)
+                set_protocol(test, Psctp);
+                client_flag = 1;
+#else /* linux */
+                i_errno = IEUNIMP;
+                return -1;
+#endif /* linux */
+            break;
+
             case 'b':
 		slash = strchr(optarg, '/');
 		if (slash) {
@@ -1058,6 +1070,8 @@
 	    cJSON_AddTrueToObject(j, "tcp");
 	else if (test->protocol->id == Pudp)
 	    cJSON_AddTrueToObject(j, "udp");
+        else if (test->protocol->id == Psctp)
+            cJSON_AddTrueToObject(j, "sctp");
 	cJSON_AddIntToObject(j, "omit", test->omit);
 	if (test->server_affinity != -1)
 	    cJSON_AddIntToObject(j, "server_affinity", test->server_affinity);
@@ -1117,6 +1131,8 @@
 	    set_protocol(test, Ptcp);
 	if ((j_p = cJSON_GetObjectItem(j, "udp")) != NULL)
 	    set_protocol(test, Pudp);
+        if ((j_p = cJSON_GetObjectItem(j, "sctp")) != NULL)
+            set_protocol(test, Psctp);
 	if ((j_p = cJSON_GetObjectItem(j, "omit")) != NULL)
 	    test->omit = j_p->valueint;
 	if ((j_p = cJSON_GetObjectItem(j, "server_affinity")) != NULL)
@@ -1447,10 +1463,32 @@
 }
 
 /**************************************************************************/
+
+struct protocol *
+protocol_new(void)
+{
+    struct protocol *proto;
+
+    proto = malloc(sizeof(struct protocol));
+    if(!proto) {
+        return NULL;
+    }
+    memset(proto, 0, sizeof(struct protocol));
+
+    return proto;
+}
+
+void
+protocol_free(struct protocol *proto)
+{
+    free(proto); 
+}
+
+/**************************************************************************/
 int
 iperf_defaults(struct iperf_test *testp)
 {
-    struct protocol *tcp, *udp;
+    struct protocol *tcp, *udp, *sctp;
 
     testp->omit = OMIT;
     testp->duration = DURATION;
@@ -1487,14 +1525,9 @@
     SLIST_INIT(&testp->streams);
     SLIST_INIT(&testp->protocols);
 
-    tcp = (struct protocol *) malloc(sizeof(struct protocol));
+    tcp = protocol_new();
     if (!tcp)
         return -1;
-    memset(tcp, 0, sizeof(struct protocol));
-    udp = (struct protocol *) malloc(sizeof(struct protocol));
-    if (!udp)
-        return -1;
-    memset(udp, 0, sizeof(struct protocol));
 
     tcp->id = Ptcp;
     tcp->name = "TCP";
@@ -1506,6 +1539,12 @@
     tcp->init = NULL;
     SLIST_INSERT_HEAD(&testp->protocols, tcp, protocols);
 
+    udp = protocol_new();
+    if (!udp) {
+        protocol_free(tcp);
+        return -1;
+    }
+
     udp->id = Pudp;
     udp->name = "UDP";
     udp->accept = iperf_udp_accept;
@@ -1518,6 +1557,24 @@
 
     set_protocol(testp, Ptcp);
 
+    sctp = protocol_new();
+    if (!sctp) {
+        protocol_free(tcp);
+        protocol_free(udp);
+        return -1;
+    }
+
+    sctp->id = Psctp;
+    sctp->name = "SCTP";
+    sctp->accept = iperf_sctp_accept;
+    sctp->listen = iperf_sctp_listen;
+    sctp->connect = iperf_sctp_connect;
+    sctp->send = iperf_sctp_send;
+    sctp->recv = iperf_sctp_recv;
+    sctp->init = iperf_sctp_init;
+
+    SLIST_INSERT_AFTER(udp, sctp, protocols);
+
     testp->on_new_stream = iperf_on_new_stream;
     testp->on_test_start = iperf_on_test_start;
     testp->on_connect = iperf_on_connect;
@@ -1799,7 +1856,7 @@
 
         start_time = timeval_diff(&sp->result->start_time,&irp->interval_start_time);
         end_time = timeval_diff(&sp->result->start_time,&irp->interval_end_time);
-	if (test->protocol->id == Ptcp) {
+	if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
 	    if (test->sender && test->sender_has_retransmits) {
 		/* Interval sum, TCP with retransmits. */
 		if (test->json_output)
@@ -1861,7 +1918,7 @@
 	iprintf(test, "%s", report_bw_separator);
 	if (test->verbose)
 	    iprintf(test, "%s", report_summary);
-	if (test->protocol->id == Ptcp) {
+	if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
 	    if (test->sender_has_retransmits)
 		iprintf(test, "%s", report_bw_retrans_header);
 	    else
@@ -1886,7 +1943,7 @@
         total_sent += bytes_sent;
         total_received += bytes_received;
 
-        if (test->protocol->id == Ptcp) {
+        if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
 	    if (test->sender_has_retransmits) {
 		total_retransmits += sp->result->stream_retrans;
 	    }
@@ -1899,7 +1956,7 @@
 	unit_snprintf(ubuf, UNIT_LEN, (double) bytes_sent, 'A');
 	bandwidth = (double) bytes_sent / (double) end_time;
 	unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
-	if (test->protocol->id == Ptcp) {
+	if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
 	    if (test->sender_has_retransmits) {
 		/* Summary, TCP with retransmits. */
 		if (test->json_output)
@@ -1941,7 +1998,7 @@
 	unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
 	bandwidth = (double) bytes_received / (double) end_time;
 	unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
-	if (test->protocol->id == Ptcp) {
+	if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
 	    if (test->json_output)
 		cJSON_AddItemToObject(json_summary_stream, "receiver", iperf_json_printf("socket: %d  start: %f  end: %f  seconds: %f  bytes: %d  bits_per_second: %f", (int64_t) sp->socket, (double) start_time, (double) end_time, (double) end_time, (int64_t) bytes_received, bandwidth * 8));
 	    else
@@ -1953,7 +2010,7 @@
         unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A');
 	bandwidth = (double) total_sent / (double) end_time;
         unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
-        if (test->protocol->id == Ptcp) {
+        if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
 	    if (test->sender_has_retransmits) {
 		/* Summary sum, TCP with retransmits. */
 		if (test->json_output)
@@ -2038,7 +2095,7 @@
 	    ** else nothing.
 	    */
 	    if (timeval_equals(&sp->result->start_time, &irp->interval_start_time)) {
-		if (test->protocol->id == Ptcp) {
+		if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
 		    if (test->sender && test->sender_has_retransmits)
 			iprintf(test, "%s", report_bw_retrans_header);
 		    else
@@ -2061,7 +2118,7 @@
     st = timeval_diff(&sp->result->start_time, &irp->interval_start_time);
     et = timeval_diff(&sp->result->start_time, &irp->interval_end_time);
     
-    if (test->protocol->id == Ptcp) {
+    if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
 	if (test->sender && test->sender_has_retransmits) {
 	    /* Interval, TCP with retransmits. */
 	    if (test->json_output)
diff -r 1536985dc854 -r 4a1ed3c2c085 src/iperf_api.h
--- a/src/iperf_api.h	Fri Dec 20 15:05:21 2013 -0800
+++ b/src/iperf_api.h	Mon Dec 30 20:01:08 2013 +0530
@@ -20,6 +20,7 @@
 /* default settings */
 #define Ptcp SOCK_STREAM
 #define Pudp SOCK_DGRAM
+#define Psctp 12
 #define DEFAULT_UDP_BLKSIZE 8192
 #define DEFAULT_TCP_BLKSIZE (128 * 1024)  /* default read/write block size */
 
@@ -280,6 +281,7 @@
     IEAFFINITY = 132,       // Unable to set CPU affinity (check perror)
     IEDAEMON = 133,	    // Unable to become a daemon process
     IESETCONGESTION = 134,  // Unable to set TCP_CONGESTION
+    IESETSCTPDISABLEFRAG = 135, //Unable to set SCTP Fragmentation (check perror)
     /* Stream errors */
     IECREATESTREAM = 200,   // Unable to create a new stream (check herror/perror)
     IEINITSTREAM = 201,     // Unable to initialize stream (check herror/perror)
diff -r 1536985dc854 -r 4a1ed3c2c085 src/iperf_error.c
--- a/src/iperf_error.c	Fri Dec 20 15:05:21 2013 -0800
+++ b/src/iperf_error.c	Mon Dec 30 20:01:08 2013 +0530
@@ -294,6 +294,11 @@
             snprintf(errstr, len, "unable to set TCP_CONGESTION");
             perr = 1;
             break;
+        case IESETSCTPDISABLEFRAG:
+            snprintf(errstr, len, "unable to set SCTP_DISABLE_FRAG");
+            perr = 1;
+            break;
+
     }
 
     if (herr || perr)
diff -r 1536985dc854 -r 4a1ed3c2c085 src/iperf_sctp.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iperf_sctp.c	Mon Dec 30 20:01:08 2013 +0530
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2009-2011, The Regents of the University of California,
+ * through Lawrence Berkeley National Laboratory (subject to receipt of any
+ * required approvals from the U.S. Dept. of Energy).  All rights reserved.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE file
+ * for complete information.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_sctp.h"
+#include "net.h"
+
+
+
+/* iperf_sctp_recv
+ *
+ * receives the data for SCTP
+ */
+int
+iperf_sctp_recv(struct iperf_stream *sp)
+{
+    int r;
+
+    r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
+    if (r < 0)
+        return r;
+
+    sp->result->bytes_received += r;
+    sp->result->bytes_received_this_interval += r;
+
+    return r;
+}
+
+
+/* iperf_sctp_send 
+ *
+ * sends the data for SCTP
+ */
+int
+iperf_sctp_send(struct iperf_stream *sp)
+{
+    int r;
+
+    r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
+    if (r < 0)
+        return r;    
+
+    sp->result->bytes_sent += r;
+    sp->result->bytes_sent_this_interval += r;
+
+    return r;
+}
+
+
+
+/* iperf_sctp_accept
+ *
+ * accept a new SCTP stream connection
+ */
+int
+iperf_sctp_accept(struct iperf_test * test)
+{
+    int     s;
+    signed char rbuf = ACCESS_DENIED;
+    char    cookie[COOKIE_SIZE];
+    socklen_t len;
+    struct sockaddr_storage addr;
+
+    len = sizeof(addr);
+    s = accept(test->listener, (struct sockaddr *) &addr, &len);
+    if (s < 0) {
+        i_errno = IESTREAMCONNECT;
+        return -1;
+    }
+
+    if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) {
+        i_errno = IERECVCOOKIE;
+        return -1;
+    }
+
+    if (strcmp(test->cookie, cookie) != 0) {
+        if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) {
+            i_errno = IESENDMESSAGE;
+            return -1;
+        }
+        close(s);
+    }
+
+    return s;
+}
+
+
+/* iperf_sctp_listen
+ *
+ * start up a listener for SCTP stream connections
+ */
+int
+iperf_sctp_listen(struct iperf_test *test)
+{
+    struct addrinfo hints, *res;
+    char portstr[6];
+    int s, opt;
+
+    close(test->listener);
+   
+    snprintf(portstr, 6, "%d", test->server_port);
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = (test->settings->domain == AF_UNSPEC ? AF_INET6 : test->settings->domain);
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_PASSIVE;
+    if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) {
+        i_errno = IESTREAMLISTEN;
+        return -1;
+    }
+
+    if ((s = socket(res->ai_family, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
+        freeaddrinfo(res);
+        i_errno = IESTREAMLISTEN;
+        return -1;
+    }
+
+    if (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET6) {
+        if (test->settings->domain == AF_UNSPEC)
+            opt = 0;
+        else if (test->settings->domain == AF_INET6)
+            opt = 1;
+        setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
+    }
+
+    opt = 1;
+    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
+        close(s);
+        freeaddrinfo(res);
+        i_errno = IEREUSEADDR;
+        return -1;
+    }
+
+    if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
+        close(s);
+        freeaddrinfo(res);
+        i_errno = IESTREAMLISTEN;
+        return -1;
+    }
+
+    freeaddrinfo(res);
+
+    if (listen(s, 5) < 0) {
+        i_errno = IESTREAMLISTEN;
+        return -1;
+    }
+
+    test->listener = s;
+  
+    return s;
+}
+
+
+/* iperf_sctp_connect
+ *
+ * connect to a SCTP stream listener
+ */
+int
+iperf_sctp_connect(struct iperf_test *test)
+{
+    int s, opt;
+    char portstr[6];
+    struct addrinfo hints, *local_res, *server_res;
+
+    if (test->bind_address) {
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_family = test->settings->domain;
+        hints.ai_socktype = SOCK_STREAM;
+        if (getaddrinfo(test->bind_address, NULL, &hints, &local_res) != 0) {
+            i_errno = IESTREAMCONNECT;
+            return -1;
+        }
+    }
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = test->settings->domain;
+    hints.ai_socktype = SOCK_STREAM;
+    snprintf(portstr, sizeof(portstr), "%d", test->server_port);
+    if (getaddrinfo(test->server_hostname, portstr, &hints, &server_res) != 0) {
+	if (test->bind_address)
+	    freeaddrinfo(local_res);
+        i_errno = IESTREAMCONNECT;
+        return -1;
+    }
+
+    s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
+    if (s < 0) {
+	if (test->bind_address)
+	    freeaddrinfo(local_res);
+	freeaddrinfo(server_res);
+        i_errno = IESTREAMCONNECT;
+        return -1;
+    }
+
+   
+    if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
+	close(s);
+	freeaddrinfo(server_res);
+        i_errno = IESTREAMCONNECT;
+        return -1;
+    }
+    freeaddrinfo(server_res);
+
+    /* Send cookie for verification */
+    if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) {
+	close(s);
+        i_errno = IESENDCOOKIE;
+        return -1;
+    }
+
+    opt = 0;
+    if(setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &opt, sizeof(opt)) < 0) {
+        close(s);
+        freeaddrinfo(server_res);
+        i_errno = IESETSCTPDISABLEFRAG;
+        return -1;
+    }
+
+    return s;
+}
+
+
+
+int
+iperf_sctp_init(struct iperf_test *test)
+{
+    return 0;
+}
+
diff -r 1536985dc854 -r 4a1ed3c2c085 src/iperf_sctp.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iperf_sctp.h	Mon Dec 30 20:01:08 2013 +0530
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2009-2011, The Regents of the University of California,
+ * through Lawrence Berkeley National Laboratory (subject to receipt of any
+ * required approvals from the U.S. Dept. of Energy).  All rights reserved.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE file
+ * for complete information.
+ */
+
+#ifndef        IPERF_SCTP_H
+#define        IPERF_SCTP_H
+
+
+#ifndef SCTP_DISABLE_FRAGMENTS
+#define SCTP_DISABLE_FRAGMENTS  8
+#endif
+
+/**
+ * iperf_sctp_accept -- accepts a new SCTP connection
+ * on sctp_listener_socket for SCTP data and param/result
+ * exchange messages
+ *returns 0 on success
+ *
+ */
+int iperf_sctp_accept(struct iperf_test *);
+
+/**
+ * iperf_sctp_recv -- receives the data for sctp
+ * and the Param/result message exchange
+ *returns state of packet received
+ *
+ */
+int iperf_sctp_recv(struct iperf_stream *);
+
+
+/**
+ * iperf_sctp_send -- sends the client data for sctp
+ * and the  Param/result message exchanges
+ * returns: bytes sent
+ *
+ */
+int iperf_sctp_send(struct iperf_stream *);
+
+
+int iperf_sctp_listen(struct iperf_test *);
+
+int iperf_sctp_connect(struct iperf_test *);
+
+int iperf_sctp_init(struct iperf_test *test);
+
+#endif
diff -r 1536985dc854 -r 4a1ed3c2c085 src/locale.c
--- a/src/locale.c	Fri Dec 20 15:05:21 2013 -0800
+++ b/src/locale.c	Mon Dec 30 20:01:08 2013 +0530
@@ -83,6 +83,9 @@
                            "  -D, --daemon              run the server as a daemon\n"
                            "Client specific:\n"
                            "  -c, --client    <host>    run in client mode, connecting to <host>\n"
+#if defined(linux)
+                           "  -x, --sctp                use sctp\n"
+#endif
                            "  -u, --udp                 use UDP rather than TCP\n"
                            "  -b, --bandwidth #[KMG][/#] target bandwidth in bits/sec\n"
                            "                            (default %d Mbit/sec for UDP, unlimited for TCP)\n"
