diff --git a/Makefile b/Makefile
index f0cb9ac..54633b5 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ cleanall: checkmakefiles
 	rm -f src/Makefile
 
 makefiles:
-	cd src && opp_makemake -f --deep --make-so -o inet -O out $$NSC_VERSION_DEF
+	cd src && opp_makemake -f --deep -lpcap -lssl -lcrypto -DHAVE_PCAP -DUSE_TF -o inet -O out $$NSC_VERSION_DEF
 
 checkmakefiles:
 	@if [ ! -f src/Makefile ]; then \
diff --git a/examples/sctp/newfeatures/README b/examples/sctp/newfeatures/README
new file mode 100644
index 0000000..8235831
--- /dev/null
+++ b/examples/sctp/newfeatures/README
@@ -0,0 +1,32 @@
+This SCTP example contains a small test setup using a dual-homed
+environment:
+- 5 Clients (#0 to #4) send 10,000 packets to 5 Servers each
+- All clients use SACK Immediately, NR-SACK and Packet Drop Reporting
+   => NR-SACKs will be used to acknowledge DATA chunks
+- Networks have a bit error rate of 10e-6
+   => PKTDROPs will be used to report bit errors
+- Client #0 applies Add-IP Set Primary at t=10s
+   => ASCONF/ASCONF_ACK will be transmitted
+- Client #1 uses PR-SCTP (i.e. unreliable transmission)
+   => FORWARD_TSNs will be generated
+
+See the omnetpp.ini file for how to configure the new SCTP extensions!
+
+
+
+Run the example as follows to print the throughputs of the servers:
+
+./run -u Cmdenv -f omnetpp.ini && \
+grep "throughput" scalars.sca | grep newFeaturesTest.serverMain
+
+(This is performed by the run-test-and-display-results script.)
+
+
+
+The resulting output should look like this:
+...
+scalar newFeaturesTest.serverMain[0].sctpApp[0]         throughput      1916071.146545
+scalar newFeaturesTest.serverMain[1].sctpApp[0]         throughput      1798230.6879317
+scalar newFeaturesTest.serverMain[2].sctpApp[0]         throughput      2014921.5453285
+scalar newFeaturesTest.serverMain[3].sctpApp[0]         throughput      1959880.7736224
+scalar newFeaturesTest.serverMain[4].sctpApp[0]         throughput      1929689.8109524
diff --git a/examples/sctp/newfeatures/newfeatures.ned b/examples/sctp/newfeatures/newfeatures.ned
new file mode 100644
index 0000000..2bd23b7
--- /dev/null
+++ b/examples/sctp/newfeatures/newfeatures.ned
@@ -0,0 +1,144 @@
+// * --------------------------------------------------------------------------
+// *
+// *     //====//  //===== <===//===>  //====//
+// *    //        //          //      //    //    SCTP Optimization Project
+// *   //=====   //          //      //====//   ==============================
+// *        //  //          //      //           University of Duisburg-Essen
+// *  =====//  //=====     //      //
+// *
+// * --------------------------------------------------------------------------
+// *
+// *   Copyright (C) 2009-2012 by Thomas Dreibholz
+// *
+// *   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 3 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+// *
+// *   Contact: dreibh@iem.uni-due.de
+
+package inet.examples.sctp.newfeatures;
+
+import inet.nodes.inet.StandardHost;
+import inet.nodes.inet.Router;
+import ned.DatarateChannel;
+import ned.DelayChannel;
+import ned.IdealChannel;
+import inet.networklayer.autorouting.MultihomedFlatNetworkConfigurator;
+
+
+channel coreChannel extends DatarateChannel
+{
+   int netID = default(0);        // NOTE: Shared by all networks!
+   datarate  = default(1 Mbps);
+   delay     = default(0ms);
+   ber       = default(0);
+   per       = default(0);
+   @display("ls=black,3");
+}
+
+channel accessChannel extends DatarateChannel
+{
+   int netID = default(1);
+   datarate  = default(1 Gbps);
+   delay     = default(0.1ms);
+   ber       = default(0);
+   per       = default(0);
+   @display("ls=blue,5");
+}
+
+
+network newFeaturesTest
+{
+    // ====== System Parameters =============================================
+    parameters:
+        int networks = default(1);
+        int systems  = default(3);
+    @display("bgi=maps/world,s");
+
+    // ====== Network Components ============================================
+   submodules:
+      // ------ Routing Auto-Configuration --------------------------------
+      configurator: MultihomedFlatNetworkConfigurator {
+         parameters:
+            @display("p=850,75;i=abstract/penguin_l,gold");
+      }
+
+      // ------ Routers -----------------------------------------------------
+      westernBorderRouter[networks] : StandardHost {
+         parameters:
+            IPForward = true;
+            @display("p=500,150,c,150;i=abstract/router_l,gold");
+         gates:
+            pppg[1 + systems];
+      }
+      westernCoreRouter[networks] : StandardHost {
+         parameters:
+            IPForward = true;
+            @display("p=700,150,c,150;i=abstract/router_l,gold");
+         gates:
+            pppg[2];
+      }
+      easternCoreRouter[networks] : StandardHost {
+         parameters:
+            IPForward = true;
+            @display("p=1000,150,c,150;i=abstract/router_l,gold");
+         gates:
+            pppg[2];
+      }
+      easternBorderRouter[networks] : StandardHost {
+         parameters:
+            IPForward = true;
+            @display("p=1200,150,c,150;i=abstract/router_l,gold");
+         gates:
+            pppg[1 + systems];
+      }
+
+      // ------ Systems -----------------------------------------------------
+      clientMain[systems] : StandardHost {
+         parameters:
+            IPForward = false;
+            @display("p=50,50,c,200;i=device/laptop_l,green");
+         gates:
+            pppg[networks];
+      }
+      serverMain[systems] : StandardHost {
+         parameters:
+            IPForward = false;
+            @display("p=1650,50,c,200;i=device/server_l,green");
+         gates:
+            pppg[networks];
+      }
+
+   // ====== Connections ====================================================
+   connections:
+      for n=0..networks-1 {
+         westernCoreRouter[n].pppg[0]
+            <--> coreChannel { netID=1000+n; @display("ls=blue,3"); }
+            <--> easternCoreRouter[n].pppg[0];
+
+         westernBorderRouter[n].pppg[0]
+            <--> accessChannel { netID=1000+n; @display("ls=blue,5"); }
+            <--> westernCoreRouter[n].pppg[1];
+         easternBorderRouter[n].pppg[0]
+            <--> accessChannel { netID=1000+n; @display("ls=blue,5"); }
+            <--> easternCoreRouter[n].pppg[1];
+      }
+
+      for s=0..systems-1, for n=0..networks-1 {
+            clientMain[s].pppg[n]
+               <--> accessChannel { netID=1000+n; @display("ls=green,5"); }
+               <--> westernBorderRouter[n].pppg[1 + s];
+            serverMain[s].pppg[n]
+               <--> accessChannel { netID=1000+n; @display("ls=green,5"); }
+               <--> easternBorderRouter[n].pppg[1 + s];
+      }
+}
diff --git a/examples/sctp/newfeatures/omnetpp.ini b/examples/sctp/newfeatures/omnetpp.ini
new file mode 100644
index 0000000..58dafd2
--- /dev/null
+++ b/examples/sctp/newfeatures/omnetpp.ini
@@ -0,0 +1,143 @@
+# * --------------------------------------------------------------------------
+# *
+# *     //====//  //===== <===//===>  //====//
+# *    //        //          //      //    //    SCTP Optimization Project
+# *   //=====   //          //      //====//   ==============================
+# *        //  //          //      //           University of Duisburg-Essen
+# *  =====//  //=====     //      //
+# *
+# * --------------------------------------------------------------------------
+# *
+# *   Copyright (C) 2009-2012 by Thomas Dreibholz
+# *
+# *   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 3 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+# *
+# *   Contact: dreibh@iem.uni-due.de
+
+[General]
+network             = newFeaturesTest
+rng-class           = cMersenneTwister
+seed-0-mt           = 1
+output-scalar-file  = scalars.sca
+output-vector-file  = vectors.vec
+sim-time-limit      = 0s 3600s 1ms
+simtime-scale       = -8   # 10ns time scale
+
+cmdenv-express-mode = yes     # *** Enable debugging messages here! ***
+**.testing          = false   # *** Enable debugging messages here! ***
+**.checkQueues      = false   # *** Enable checkOutstandingBytes() calls here! ***
+
+newFeaturesTest.**.vector-recording           = no
+newFeaturesTest.**.vector-recording-intervals = 0s..0s 3600s
+
+
+# ===== General Scenario Settings ======================
+newFeaturesTest.systems  = 5
+newFeaturesTest.networks = 2
+
+# ===== Network QoS Settings ===========================
+newFeaturesTest.westernCoreRouter[*].ppp[0].ppp.mtu             = 1500
+newFeaturesTest.westernCoreRouter[*].ppp[0].queueType           = "REDQueue"
+newFeaturesTest.westernCoreRouter[*].ppp[0].queue.wq            = 0.002
+newFeaturesTest.westernCoreRouter[*].ppp[0].queue.minth         = 30
+newFeaturesTest.westernCoreRouter[*].ppp[0].queue.maxth         = 90
+newFeaturesTest.westernCoreRouter[*].ppp[0].queue.maxp          = 0.1
+newFeaturesTest.westernCoreRouter[*].ppp[0].queue.frameCapacity = 100
+newFeaturesTest.westernCoreRouter[*].ppp[0].queue.pkrate        = 8333.333
+newFeaturesTest.westernCoreRouter[*].pppg$o[0].channel.datarate = 1e+07 bps
+newFeaturesTest.westernCoreRouter[*].pppg$o[0].channel.delay    = 10 ms
+newFeaturesTest.westernCoreRouter[*].pppg$o[0].channel.per      = 0
+newFeaturesTest.westernCoreRouter[*].pppg$o[0].channel.ber      = 1e-7   ## !!!
+newFeaturesTest.easternCoreRouter[*].ppp[0].ppp.mtu             = 1500
+newFeaturesTest.easternCoreRouter[*].ppp[0].queueType           = "REDQueue"
+newFeaturesTest.easternCoreRouter[*].ppp[0].queue.wq            = 0.002
+newFeaturesTest.easternCoreRouter[*].ppp[0].queue.minth         = 30
+newFeaturesTest.easternCoreRouter[*].ppp[0].queue.maxth         = 90
+newFeaturesTest.easternCoreRouter[*].ppp[0].queue.maxp          = 0.1
+newFeaturesTest.easternCoreRouter[*].ppp[0].queue.frameCapacity = 100
+newFeaturesTest.easternCoreRouter[*].ppp[0].queue.pkrate        = 8333.333
+newFeaturesTest.easternCoreRouter[*].pppg$o[0].channel.datarate = 1e+07 bps
+newFeaturesTest.easternCoreRouter[*].pppg$o[0].channel.delay    = 10 ms
+newFeaturesTest.easternCoreRouter[*].pppg$o[0].channel.per      = 0
+newFeaturesTest.easternCoreRouter[*].pppg$o[0].channel.ber      = 1e-7   ## !!!
+
+# ----- Common Settings ----------------------------------
+newFeaturesTest.*.ppp[*].ppp.mtu = 1500
+
+# ===== Application Parameters ===========================
+# ----- SCTP Clients -------------------------------------
+newFeaturesTest.clientMain[*].numSctpApps                      = 1
+newFeaturesTest.clientMain[*].sctpAppType                      = "SCTPClient"
+newFeaturesTest.clientMain[*].sctpApp[*].startTime             = 1s
+newFeaturesTest.clientMain[*].sctpApp[*].numRequestsPerSession = 10000
+newFeaturesTest.clientMain[*].sctpApp[*].requestLength         = 1000
+
+newFeaturesTest.clientMain[*].sctpApp[*].address               = ""
+newFeaturesTest.clientMain[*].sctpApp[*].port                  = 20000+${mainClientAppID=ancestorIndex(0)}
+newFeaturesTest.clientMain[*].sctpApp[*].connectAddress        = "newFeaturesTest.serverMain[" + string(${mainClientID=ancestorIndex(1)}) + "]/ppp0"
+newFeaturesTest.clientMain[*].sctpApp[*].connectPort           = 8000+${mainClientAppID}
+newFeaturesTest.clientMain[*].sctpApp[*].primaryPath           = "newFeaturesTest.serverMain[" + string(${mainClientID}) + "]/ppp0"
+
+
+# ----- SCTP Servers--------------------------------------
+newFeaturesTest.serverMain[*].numSctpApps                             = 1
+newFeaturesTest.serverMain[*].sctpAppType                             = "SCTPServer"
+newFeaturesTest.serverMain[*].sctpApp[*].numPacketsToReceivePerClient = 10000
+
+newFeaturesTest.serverMain[*].sctpApp[*].address                      = ""
+newFeaturesTest.serverMain[*].sctpApp[*].port                         = 8000+${mainServerAppID=ancestorIndex(0)}
+
+# ===== TCPDump ==========================================
+newFeaturesTest.client*[*].tcpdump.dumpFile           = ${N1=fullPath()}+".pcap"
+newFeaturesTest.server*[*].tcpdump.dumpFile           = ${N2=fullPath()}+".pcap"
+newFeaturesTest.westernCoreRouter[*].tcpdump.dumpFile = ${N3=fullPath()}+".pcap"
+newFeaturesTest.easternCoreRouter[*].tcpdump.dumpFile = ${N4=fullPath()}+".pcap"
+
+# ===== SCTP Parameters ==================================
+newFeaturesTest.*.sctp.arwnd            = 5e+06
+newFeaturesTest.*.sctp.hbInterval       = 30 s
+newFeaturesTest.*.sctp.enableHeartbeats = true
+
+# ===== SCTP Extensions ==================================
+
+# ------ SACK Immediately --------------------------------
+newFeaturesTest.*.sctp.sackNow = true
+
+# ------ Non-Renegable SACK (NR-SACK) --------------------
+newFeaturesTest.*.sctp.nrSack = true
+
+# ------ Packet Drop Reporting (PKTDROP) -----------------
+newFeaturesTest.*.sctp.packetDrop = true
+
+# ------ Chunk Authentication (AUTH)----------------------
+newFeaturesTest.*.sctp.auth = true
+# Which chunks should be authenticated?
+newFeaturesTest.*.sctp.chunks = "ASCONF,ASCONF_ACK"
+
+# ------ Dynamic Address Reconfiguration ("Add-IP")-------
+newFeaturesTest.*.sctp.addIP = true
+
+newFeaturesTest.clientMain[0].sctp.addTime    = 10s
+newFeaturesTest.clientMain[0].sctp.addAddress = "newFeaturesTest.clientMain[" + string(${mainClientID}) + "]/ppp1"
+newFeaturesTest.clientMain[0].sctp.addIpType  = "49156"   # SET_PRIMARY_ADDRESS
+# ++++++ Add-IP example is for Client #0 only! ++++++
+
+# ------ Partial Reliability (PR-SCTP) -------------------
+# prMethod condigures the PR-SCTP method:
+#   0=NONE, 1=PR_TTL, 2=PR_RTX, 3=PR_PRIO, 4=PR_STRRST
+# prValue configures the corresponding parameter
+# (e.g. maximum number of retransmission for PR_RTX)
+newFeaturesTest.clientMain[1].sctpApp[*].prMethod = 2   # PR_RTX
+newFeaturesTest.clientMain[1].sctpApp[*].prValue  = 0   # 0 retransmissions
+# ++++++ PR-SCTP example is for Client #1 only! ++++++
diff --git a/examples/sctp/newfeatures/run b/examples/sctp/newfeatures/run
new file mode 100755
index 0000000..120ea5e
--- /dev/null
+++ b/examples/sctp/newfeatures/run
@@ -0,0 +1 @@
+../../../src/inet -n ../..:../../../src $*
diff --git a/examples/sctp/newfeatures/run-test-and-display-results b/examples/sctp/newfeatures/run-test-and-display-results
new file mode 100755
index 0000000..8cd7b7c
--- /dev/null
+++ b/examples/sctp/newfeatures/run-test-and-display-results
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+./run -u Cmdenv -f omnetpp.ini && \
+grep "throughput" scalars.sca | grep newFeaturesTest.serverMain
diff --git a/src/applications/sctpapp/SCTPClient.cc b/src/applications/sctpapp/SCTPClient.cc
index 0668b23..3b5b76a 100644
--- a/src/applications/sctpapp/SCTPClient.cc
+++ b/src/applications/sctpapp/SCTPClient.cc
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2009 Thomas Dreibholz
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -22,511 +22,618 @@
 #include "SCTPClient.h"
 
 #define MSGKIND_CONNECT  0
-#define MSGKIND_SEND         1
+#define MSGKIND_SEND     1
 #define MSGKIND_ABORT    2
 #define MSGKIND_PRIMARY  3
-#define MSGKIND_STOP         5
+#define MSGKIND_RESET    4
+#define MSGKIND_STOP     5
 
 
 Define_Module(SCTPClient);
 
 void SCTPClient::initialize()
 {
-    const char * address;
-    char* token;
-    AddressVector addresses;
-    sctpEV3<<"initialize SCTP Client\n";
-    numSessions = numBroken = packetsSent = packetsRcvd = bytesSent = echoedBytesSent = bytesRcvd = 0;
-    WATCH(numSessions);
-    WATCH(numBroken);
-    WATCH(packetsSent);
-    WATCH(packetsRcvd);
-    WATCH(bytesSent);
-    WATCH(bytesRcvd);
-    // parameters
-    address=par("address");
-
-    token = strtok((char*)address,",");
-    while (token != NULL)
-    {
-        addresses.push_back(IPvXAddress(token));
-        token = strtok(NULL, ",");
-    }
-    int32 port = par("port");
-    echoFactor = par("echoFactor");
-    if (!echoFactor) echoFactor = false;
-    ordered = (bool)par("ordered");
-    finishEndsSimulation = (bool)par("finishEndsSimulation");
-    if (strcmp(address,"")==0)
-    {
-        socket.bind(port);
-    }
-    else
-    {
-        socket.bindx(addresses, port);
-    }
-
-    socket.setCallbackObject(this);
-    socket.setOutputGate(gate("sctpOut"));
-    setStatusString("waiting");
-
-    timeMsg = new cMessage("CliAppTimer");
-    numRequestsToSend = 0;
-    numPacketsToReceive = 0;
-    queueSize = par("queueSize");
-    WATCH(numRequestsToSend);
-    recordScalar("ums", (uint32) par("requestLength"));
-    timeMsg->setKind(MSGKIND_CONNECT);
-    scheduleAt((simtime_t)par("startTime"), timeMsg);
-    sendAllowed = true;
-    bufferSize = 0;
-    if ((simtime_t)par("stopTime")!=0)
-    {
-        stopTimer = new cMessage("StopTimer");
-        stopTimer->setKind(MSGKIND_STOP);
-        scheduleAt((simtime_t)par("stopTime"), stopTimer);
-        timer = true;
-    }
-    else
-    {
-        timer = false;
-        stopTimer = NULL;
-    }
-    if ((simtime_t)par("primaryTime")!=0)
-    {
-        primaryChangeTimer = new cMessage("PrimaryTime");
-        primaryChangeTimer->setKind(MSGKIND_PRIMARY);
-        scheduleAt((simtime_t)par("primaryTime"), primaryChangeTimer);
-    }
-    else
-    {
-        primaryChangeTimer = NULL;
-    }
+   const char * address;
+   char* token;
+   AddressVector addresses;
+   sctpEV3<<"initialize SCTP Client\n";
+   numSessions = numBroken = packetsSent = packetsRcvd = bytesSent = echoedBytesSent = bytesRcvd = 0;
+   chunksAbandoned = 0;
+   WATCH(chunksAbandoned);
+   WATCH(numSessions);
+   WATCH(numBroken);
+   WATCH(packetsSent);
+   WATCH(packetsRcvd);
+   WATCH(bytesSent);
+   WATCH(bytesRcvd);
+   // parameters
+   address=par("address");
+
+   token = strtok((char*)address,",");
+   while (token != NULL)
+   {
+      addresses.push_back(IPvXAddress(token));
+      token = strtok(NULL, ",");
+   }
+   int32 port = par("port");
+   echoFactor = par("echoFactor");
+   if (!echoFactor) echoFactor = false;
+   ordered = (bool)par("ordered");
+   finishEndsSimulation = (bool)par("finishEndsSimulation");
+   if (strcmp(address,"")==0)
+   {
+      socket.bind(port);
+   }
+   else
+   {
+      socket.bindx(addresses, port);
+   }
+
+   socket.setCallbackObject(this);
+   socket.setOutputGate(gate("sctpOut"));
+   setStatusString("waiting");
+
+   timeMsg = new cMessage("CliAppTimer");
+   numRequestsToSend = 0;
+   numPacketsToReceive = 0;
+   queueSize = par("queueSize");
+   WATCH(numRequestsToSend);
+   recordScalar("ums", (uint32) par("requestLength"));
+   timeMsg->setKind(MSGKIND_CONNECT);
+   scheduleAt((simtime_t)par("startTime"), timeMsg);
+   sendAllowed = true;
+   bufferSize = 0;
+   if ((simtime_t)par("stopTime")!=0)
+   {
+      stopTimer = new cMessage("StopTimer");
+      stopTimer->setKind(MSGKIND_STOP);
+      scheduleAt((simtime_t)par("stopTime"), stopTimer);
+      timer = true;
+   }
+   else
+   {
+      timer = false;
+      stopTimer = NULL;
+   }
+   if ((simtime_t)par("primaryTime")!=0)
+   {
+      primaryChangeTimer = new cMessage("PrimaryTime");
+      primaryChangeTimer->setKind(MSGKIND_PRIMARY);
+      scheduleAt((simtime_t)par("primaryTime"), primaryChangeTimer);
+   }
+   else
+   {
+      primaryChangeTimer = NULL;
+   }
 }
 
 void SCTPClient::handleMessage(cMessage *msg)
 {
-    if (msg->isSelfMessage())
-        handleTimer(msg);
-    else
-    {
-        socket.processMessage(PK(msg));
-    }
+   if (msg->isSelfMessage())
+      handleTimer(msg);
+   else
+   {
+      socket.processMessage(PK(msg));
+   }
 }
 
 void SCTPClient::connect()
 {
-    const char *connectAddress = par("connectAddress");
-    int32 connectPort = par("connectPort");
-    inStreams = par("inboundStreams");
-    outStreams = par("outboundStreams");
-    socket.setInboundStreams(inStreams);
-    socket.setOutboundStreams(outStreams);
-    ev << "issuing OPEN command\n";
-    setStatusString("connecting");
-    ev<<"connect to address "<<connectAddress<<"\n";
-    socket.connect(IPAddressResolver().resolve(connectAddress, 1), connectPort, (uint32)par("numRequestsPerSession"));
-    numSessions++;
+   const char *connectAddress = par("connectAddress");
+   int32 connectPort = par("connectPort");
+   inStreams = par("inboundStreams");
+   outStreams = par("outboundStreams");
+   socket.setInboundStreams(inStreams);
+   socket.setOutboundStreams(outStreams);
+   ev << "issuing OPEN command\n";
+   setStatusString("connecting");
+   ev<<"connect to address "<<connectAddress<<"\n";
+   bool streamReset = par("streamReset");
+   socket.connect(IPAddressResolver().resolve(connectAddress, 1), connectPort, streamReset, (int32)par("prMethod"), (uint32)par("numRequestsPerSession"));
+
+   if (!streamReset)
+      streamReset = false;
+   else if (streamReset == true)
+   {
+      cMessage* cmsg = new cMessage("StreamReset");
+      cmsg->setKind(MSGKIND_RESET);
+      sctpEV3<<"StreamReset Timer scheduled at "<<simulation.getSimTime()<<"\n";
+      scheduleAt(simulation.getSimTime()+(double)par("streamRequestTime"), cmsg);
+   }
+
+   for (uint16 i = 0; i < outStreams; i++)
+   {
+      streamRequestLengthMap[i] = par("requestLength");
+      streamRequestRatioMap[i] = 1;
+      streamRequestRatioSendMap[i] = 1;
+   }
+
+   uint32 streamNum = 0;
+   cStringTokenizer requestTokenizer(par("streamRequestLengths").stringValue());
+   while (requestTokenizer.hasMoreTokens())
+   {
+      const char *token = requestTokenizer.nextToken();
+      streamRequestLengthMap[streamNum] = (uint32) atoi(token);
+
+      streamNum++;
+   }
+
+   streamNum = 0;
+   cStringTokenizer ratioTokenizer(par("streamRequestRatio").stringValue());
+   while (ratioTokenizer.hasMoreTokens())
+   {
+      const char *token = ratioTokenizer.nextToken();
+      streamRequestRatioMap[streamNum] = (uint32) atoi(token);
+      streamRequestRatioSendMap[streamNum] = (uint32) atoi(token);
+
+      streamNum++;
+   }
+
+   streamNum = 0;
+   cStringTokenizer prioTokenizer(par("streamPriorities").stringValue());
+   while (prioTokenizer.hasMoreTokens())
+   {
+      const char *token = prioTokenizer.nextToken();
+      socket.setStreamPriority(streamNum, (uint32) atoi(token));
+
+      streamNum++;
+   }
+   numSessions++;
 }
 
 void SCTPClient::close()
 {
-    setStatusString("closing");
-    socket.close();
+   setStatusString("closing");
+   socket.close();
 }
 
 
 void SCTPClient::setStatusString(const char *s)
 {
-    if (ev.isGUI()) getDisplayString().setTagArg("t", 0, s);
+   if (ev.isGUI()) getDisplayString().setTagArg("t", 0, s);
 }
 
 void SCTPClient::socketEstablished(int32, void *, uint64 buffer )
 {
-      int32 count = 0;
-     ev<<"SCTPClient: connected\n";
-    setStatusString("connected");
-    bufferSize = buffer;
-    // determine number of requests in this session
-    numRequestsToSend = (long) par("numRequestsPerSession");
-    numPacketsToReceive = (long) par("numPacketsToReceive");
-    if (numRequestsToSend<1)
-        numRequestsToSend = 0;
-        sctpEV3<<"SCTPClient:numRequestsToSend="<<numRequestsToSend<<"\n";
-    // perform first request (next one will be sent when reply arrives)
-    if ((numRequestsToSend>0 && !timer) || timer)
-    {
-        if ((simtime_t)par("thinkTime") > 0)
-        {
-            if (sendAllowed)
-            {
-                sendRequest();
-                if (!timer)
-                    numRequestsToSend--;
-            }
-            timeMsg->setKind(MSGKIND_SEND);
-            scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeMsg);
-        }
-        else
-        {
-            if (queueSize>0)
-            {
-                while (((!timer && numRequestsToSend > 0) || timer) && count++ < queueSize*2 && sendAllowed)
-                {
-                    if (count == queueSize*2)
-                        sendRequest();
-                    else
-                        sendRequest(false);
-                    if (!timer)
-                    {
-                        if (--numRequestsToSend == 0)
-                            sendAllowed = false;
-                    }
-                }
-                if (((!timer && numRequestsToSend>0) || timer) && sendAllowed)
-                    sendQueueRequest();
-            }
-            else
+     int32 count = 0;
+    ev<<"SCTPClient: connected\n";
+   setStatusString("connected");
+   bufferSize = buffer;
+   // determine number of requests in this session
+   numRequestsToSend = (long) par("numRequestsPerSession");
+   numPacketsToReceive = (long) par("numPacketsToReceive");
+   if (numRequestsToSend<1)
+      numRequestsToSend = 0;
+      sctpEV3<<"SCTPClient:numRequestsToSend="<<numRequestsToSend<<"\n";
+   // perform first request (next one will be sent when reply arrives)
+   if ((numRequestsToSend>0 && !timer) || timer)
+   {
+      if ((simtime_t)par("thinkTime") > 0)
+      {
+         if (sendAllowed)
+         {
+            sendRequest();
+            if (!timer)
+               numRequestsToSend--;
+         }
+         timeMsg->setKind(MSGKIND_SEND);
+         scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeMsg);
+      }
+      else
+      {
+         if (queueSize>0)
+         {
+            while (((!timer && numRequestsToSend > 0) || timer) && count++ < queueSize*2 && sendAllowed)
             {
-                while ((((!timer && numRequestsToSend>0) || timer) && sendAllowed && bufferSize>0) ||
-                    (((!timer && numRequestsToSend>0) || timer) && sendAllowed && buffer==0))
-                {
-                    if (!timer && numRequestsToSend==1)
-                        sendRequest(true);
-                    else
-                        sendRequest(false);
-                    if (!timer && (--numRequestsToSend == 0))
-                            sendAllowed = false;
-                    }
-                }
+               if (count == queueSize*2)
+                  sendRequest();
+               else
+                  sendRequest(false);
+               if (!timer)
+               {
+                  if (--numRequestsToSend == 0)
+                     sendAllowed = false;
+               }
             }
-            if ((!timer && numPacketsToReceive == 0) && (simtime_t)par("waitToClose")>0)
+            if (((!timer && numRequestsToSend>0) || timer) && sendAllowed)
+               sendQueueRequest();
+         }
+         else
+         {
+            while ((((!timer && numRequestsToSend>0) || timer) && sendAllowed && bufferSize>0) ||
+            	(((!timer && numRequestsToSend>0) || timer) && sendAllowed && buffer==0))
             {
-                timeMsg->setKind(MSGKIND_ABORT);
-                scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), timeMsg);
+               if (!timer && numRequestsToSend==1)
+                  sendRequest(true);
+               else
+                  sendRequest(false);
+               if (!timer && (--numRequestsToSend == 0))
+                     sendAllowed = false;
+               }
             }
-            if ((!timer && numRequestsToSend == 0) && (simtime_t)par("waitToClose")==0)
-            {
-                sctpEV3<<"socketEstablished:no more packets to send, call shutdown\n";
-                socket.shutdown();
-                if (timeMsg->isScheduled())
-                    cancelEvent(timeMsg);
-                if (finishEndsSimulation) {
-                    endSimulation();
-                }
+         }
+         if ((!timer && numPacketsToReceive == 0) && (simtime_t)par("waitToClose")>0)
+         {
+            timeMsg->setKind(MSGKIND_ABORT);
+            scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), timeMsg);
+         }
+         if ((!timer && numRequestsToSend == 0) && (simtime_t)par("waitToClose")==0)
+         {
+            sctpEV3<<"socketEstablished:no more packets to send, call shutdown\n";
+            socket.shutdown();
+            if (timeMsg->isScheduled())
+               cancelEvent(timeMsg);
+            if (finishEndsSimulation) {
+               endSimulation();
             }
-        }
+         }
+      }
 }
 
 void SCTPClient::sendQueueRequest()
 {
-    cPacket* cmsg = new cPacket("Queue");
-    SCTPInfo* qinfo = new SCTPInfo();
-    qinfo->setText(queueSize);
-    cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
-    qinfo->setAssocId(socket.getConnectionId());
-    cmsg->setControlInfo(qinfo);
-        sctpEV3 << "Sending queue request ..." << endl;
-    socket.sendRequest(cmsg);
+   cPacket* cmsg = new cPacket("Queue");
+   SCTPInfo* qinfo = new SCTPInfo();
+   qinfo->setText(queueSize);
+   cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
+   qinfo->setAssocId(socket.getConnectionId());
+   cmsg->setControlInfo(qinfo);
+      sctpEV3 << "Sending queue request ..." << endl;
+   socket.sendRequest(cmsg);
 }
 
 void SCTPClient::sendRequestArrived()
 {
-    int32 count = 0;
-
-    sctpEV3<<"sendRequestArrived numRequestsToSend="<<numRequestsToSend<<"\n";
-    while (((!timer && numRequestsToSend > 0) || timer) && count++ < queueSize && sendAllowed)
-    {
-        if (count == queueSize)
-            sendRequest();
-        else
-            sendRequest(false);
-
-        if (!timer)
-            numRequestsToSend--;
-        if ((!timer && numRequestsToSend == 0))
-        {
-            sctpEV3<<"no more packets to send, call shutdown\n";
-            socket.shutdown();
-            if (timeMsg->isScheduled())
-                cancelEvent(timeMsg);
-            if (finishEndsSimulation) {
-                endSimulation();
-            }
-        }
-    }
+   int32 count = 0;
+
+   sctpEV3<<"sendRequestArrived numRequestsToSend="<<numRequestsToSend<<"\n";
+   while (((!timer && numRequestsToSend > 0) || timer) && count++ < queueSize && sendAllowed)
+   {
+      if (count == queueSize)
+         sendRequest();
+      else
+         sendRequest(false);
+
+      if (!timer)
+         numRequestsToSend--;
+      if ((!timer && numRequestsToSend == 0))
+      {
+         sctpEV3<<"no more packets to send, call shutdown\n";
+         socket.shutdown();
+         if (timeMsg->isScheduled())
+            cancelEvent(timeMsg);
+         if (finishEndsSimulation) {
+            endSimulation();
+         }
+      }
+   }
 }
 
 void SCTPClient::socketDataArrived(int32, void *, cPacket *msg, bool)
 {
-    packetsRcvd++;
-    sctpEV3<<"Client received packet Nr "<<packetsRcvd<<" from SCTP\n";
-    SCTPCommand* ind = check_and_cast<SCTPCommand*>(msg->removeControlInfo());
-    bytesRcvd+=msg->getByteLength();
-    if (echoFactor > 0)
-    {
-        SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
-        cPacket* cmsg = new cPacket("SVData");
-        echoedBytesSent+=smsg->getBitLength()/8;
-        cmsg->encapsulate(smsg);
-        if (ind->getSendUnordered())
-            cmsg->setKind(SCTP_C_SEND_UNORDERED);
-        else
-            cmsg->setKind(SCTP_C_SEND_ORDERED);
-        packetsSent++;
-        delete msg;
-        socket.send(cmsg, 1);
-    }
-    if ((long)par("numPacketsToReceive")>0)
-    {
-        numPacketsToReceive--;
-        if (numPacketsToReceive == 0)
-        {
-            close();
-        }
-    }
-    delete ind;
+   packetsRcvd++;
+   sctpEV3<<"Client received packet Nr "<<packetsRcvd<<" from SCTP\n";
+   SCTPCommand* ind = check_and_cast<SCTPCommand*>(msg->removeControlInfo());
+   bytesRcvd+=msg->getByteLength();
+   if (echoFactor > 0)
+   {
+      SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
+      cPacket* cmsg = new cPacket("SVData");
+      echoedBytesSent+=smsg->getBitLength()/8;
+      cmsg->encapsulate(smsg);
+      if (ind->getSendUnordered())
+         cmsg->setKind(SCTP_C_SEND_UNORDERED);
+      else
+         cmsg->setKind(SCTP_C_SEND_ORDERED);
+      packetsSent++;
+      delete msg;
+      socket.send(cmsg, 0, 0, 1);
+   }
+   if ((long)par("numPacketsToReceive")>0)
+   {
+      numPacketsToReceive--;
+      if (numPacketsToReceive == 0)
+      {
+         close();
+      }
+   }
+   delete ind;
 }
 
 
 void SCTPClient::sendRequest(bool last)
 {
-    uint32 i, sendBytes;
-
-    sendBytes = par("requestLength");
-
-
-    if (sendBytes < 1)
-        sendBytes=1;
-    cPacket* cmsg = new cPacket("AppData");
-    SCTPSimpleMessage* msg=new SCTPSimpleMessage("data");
-
-    msg->setDataArraySize(sendBytes);
-    for (i=0; i<sendBytes; i++)
-    {
-        msg->setData(i, 'a');
-    }
-    msg->setDataLen(sendBytes);
-    msg->setByteLength(sendBytes);
-    msg->setCreationTime(simulation.getSimTime());
-    cmsg->encapsulate(msg);
-    if (ordered)
-        cmsg->setKind(SCTP_C_SEND_ORDERED);
-    else
-        cmsg->setKind(SCTP_C_SEND_UNORDERED);
-    // send SCTPMessage with SCTPSimpleMessage enclosed
-    sctpEV3 << "Sending request ..." << endl;
-    bufferSize -= sendBytes;
-    if (bufferSize < 0)
-        last = true;
-    socket.send(cmsg, last);
-    bytesSent+=sendBytes;
+   uint32 i, sendBytes;
+
+   sendBytes = par("requestLength");
+
+   // find next stream
+   uint16 nextStream = 0;
+   for (uint16 i = 0; i < outStreams; i++)
+   {
+      if (streamRequestRatioSendMap[i] > streamRequestRatioSendMap[nextStream])
+         nextStream = i;
+   }
+
+   // no stream left, reset map
+   if (nextStream == 0 && streamRequestRatioSendMap[nextStream] == 0)
+   {
+      for (uint16 i = 0; i < outStreams; i++)
+      {
+         streamRequestRatioSendMap[i] = streamRequestRatioMap[i];
+         if (streamRequestRatioSendMap[i] > streamRequestRatioSendMap[nextStream])
+            nextStream = i;
+      }
+   }
+
+   if (nextStream == 0 && streamRequestRatioSendMap[nextStream] == 0)
+   {
+      opp_error("Invalid setting of streamRequestRatio: only 0 weightings");
+   }
+
+   sendBytes = streamRequestLengthMap[nextStream];
+   streamRequestRatioSendMap[nextStream]--;
+
+   if (sendBytes < 1)
+      sendBytes=1;
+   cPacket* cmsg = new cPacket("AppData");
+   SCTPSimpleMessage* msg=new SCTPSimpleMessage("data");
+
+   msg->setDataArraySize(sendBytes);
+   for (i=0; i<sendBytes; i++)
+   {
+      msg->setData(i, 'a');
+   }
+   msg->setDataLen(sendBytes);
+   msg->setEncaps(false);
+   msg->setByteLength(sendBytes);
+   msg->setCreationTime(simulation.getSimTime());
+   cmsg->encapsulate(msg);
+   if (ordered)
+      cmsg->setKind(SCTP_C_SEND_ORDERED);
+   else
+      cmsg->setKind(SCTP_C_SEND_UNORDERED);
+   // send SCTPMessage with SCTPSimpleMessage enclosed
+   sctpEV3 << "Sending request ..." << endl;
+   bufferSize -= sendBytes;
+   if (bufferSize < 0)
+      last = true;
+   socket.send(cmsg, (int32)par("prMethod"),(double)par("prValue"), last, nextStream);
+   bytesSent+=sendBytes;
 }
 
 void SCTPClient::handleTimer(cMessage *msg)
 {
 
-    switch (msg->getKind())
-    {
-        case MSGKIND_CONNECT:
-            ev << "starting session call connect\n";
-            connect();
-            break;
-        case MSGKIND_SEND:
+   switch (msg->getKind())
+   {
+      case MSGKIND_CONNECT:
+         ev << "starting session call connect\n";
+         connect();
+         break;
+      case MSGKIND_SEND:
 
-            if (((!timer && numRequestsToSend>0) || timer))
+         if (((!timer && numRequestsToSend>0) || timer))
+         {
+            if (sendAllowed)
             {
-                if (sendAllowed)
-                {
-                    sendRequest();
-                    if (!timer)
-                        numRequestsToSend--;
-                }
-                if ((simtime_t)par("thinkTime") > 0)
-                    scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeMsg);
-                if ((!timer && numRequestsToSend == 0) && (simtime_t)par("waitToClose")==0)
-                {
-                    socket.shutdown();
-                    if (timeMsg->isScheduled())
-                        cancelEvent(timeMsg);
-                    if (finishEndsSimulation) {
-                        endSimulation();
-                    }
-                }
+               sendRequest();
+               if (!timer)
+                  numRequestsToSend--;
             }
-            else if ((!timer && numRequestsToSend == 0) && (simtime_t)par("waitToClose")==0)
+            if ((simtime_t)par("thinkTime") > 0)
+               scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeMsg);
+            if ((!timer && numRequestsToSend == 0) && (simtime_t)par("waitToClose")==0)
             {
-                    socket.shutdown();
-                    if (timeMsg->isScheduled())
-                        cancelEvent(timeMsg);
-                    if (finishEndsSimulation) {
-                        endSimulation();
-                    }
-            }
-            break;
-        case MSGKIND_ABORT:
-            close();
-            break;
-        case MSGKIND_PRIMARY:
-            setPrimaryPath((const char*)par("newPrimary"));
-            break;
-        case MSGKIND_STOP:
-            numRequestsToSend=0;
-            sendAllowed = false;
-            socket.abort();
-            socket.close();
-            if (timeMsg->isScheduled())
-                cancelEvent(timeMsg);
-            socket.close();
-            if (finishEndsSimulation) {
-                endSimulation();
+               socket.shutdown();
+               if (timeMsg->isScheduled())
+                  cancelEvent(timeMsg);
+               if (finishEndsSimulation) {
+                  endSimulation();
+               }
             }
-            break;
-        default:
-            ev<<"MsgKind ="<<msg->getKind()<<" unknown\n";
-            break;
-    }
+         }
+         else if ((!timer && numRequestsToSend == 0) && (simtime_t)par("waitToClose")==0)
+         {
+               socket.shutdown();
+               if (timeMsg->isScheduled())
+                  cancelEvent(timeMsg);
+               if (finishEndsSimulation) {
+                  endSimulation();
+               }
+         }
+         break;
+      case MSGKIND_ABORT:
+         close();
+         break;
+      case MSGKIND_PRIMARY:
+         setPrimaryPath((const char*)par("newPrimary"));
+         break;
+      case MSGKIND_RESET:
+         sctpEV3<<"StreamReset Timer expired at Client at "<<simulation.getSimTime()<<"...send notification\n";
+         sendStreamResetNotification();
+         break;
+      case MSGKIND_STOP:
+         numRequestsToSend=0;
+         sendAllowed = false;
+         socket.abort();
+         socket.close();
+         if (timeMsg->isScheduled())
+            cancelEvent(timeMsg);
+         socket.close();
+         if (finishEndsSimulation) {
+            endSimulation();
+         }
+         break;
+      default:
+         ev<<"MsgKind ="<<msg->getKind()<<" unknown\n";
+         break;
+   }
 }
 
 
 void SCTPClient::socketDataNotificationArrived(int32 connId, void *ptr, cPacket *msg)
 {
-    SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
-    cPacket* cmsg = new cPacket("CMSG-DataArr");
-    SCTPSendCommand *cmd = new SCTPSendCommand();
-    cmd->setAssocId(ind->getAssocId());
-    cmd->setSid(ind->getSid());
-    cmd->setNumMsgs(ind->getNumMsgs());
-    cmsg->setKind(SCTP_C_RECEIVE);
-    cmsg->setControlInfo(cmd);
-    delete ind;
-    socket.sendNotification(cmsg);
+   SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+   cPacket* cmsg = new cPacket("CMSG-DataArr");
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(ind->getAssocId());
+   cmd->setSid(ind->getSid());
+   cmd->setNumMsgs(ind->getNumMsgs());
+   cmsg->setKind(SCTP_C_RECEIVE);
+   cmsg->setControlInfo(cmd);
+   delete ind;
+   socket.sendNotification(cmsg);
 }
 
 void SCTPClient::shutdownReceivedArrived(int32 connId)
 {
-    if (numRequestsToSend==0)
-    {
-        cPacket* cmsg = new cPacket("Request");
-        SCTPInfo* qinfo = new SCTPInfo();
-        cmsg->setKind(SCTP_C_NO_OUTSTANDING);
-        qinfo->setAssocId(connId);
-        cmsg->setControlInfo(qinfo);
-        socket.sendNotification(cmsg);
-    }
+   if (numRequestsToSend==0)
+   {
+      cPacket* cmsg = new cPacket("Request");
+      SCTPInfo* qinfo = new SCTPInfo();
+      cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+      qinfo->setAssocId(connId);
+      cmsg->setControlInfo(qinfo);
+      socket.sendNotification(cmsg);
+   }
 }
 
 void SCTPClient::socketPeerClosed(int32, void *)
 {
-    // close the connection (if not already closed)
-    if (socket.getState()==SCTPSocket::PEER_CLOSED)
-    {
-        ev << "remote SCTP closed, closing here as well\n";
-        close();
-    }
+   // close the connection (if not already closed)
+   if (socket.getState()==SCTPSocket::PEER_CLOSED)
+   {
+      ev << "remote SCTP closed, closing here as well\n";
+      close();
+   }
 }
 
 void SCTPClient::socketClosed(int32, void *)
 {
-    // *redefine* to start another session etc.
-    ev << "connection closed\n";
-    setStatusString("closed");
-    if (primaryChangeTimer)
-    {
-        cancelEvent(primaryChangeTimer);
-        delete primaryChangeTimer;
-        primaryChangeTimer = NULL;
-    }
+   // *redefine* to start another session etc.
+   ev << "connection closed\n";
+   setStatusString("closed");
+   if (primaryChangeTimer)
+   {
+      cancelEvent(primaryChangeTimer);
+      delete primaryChangeTimer;
+      primaryChangeTimer = NULL;
+   }
 }
 
 void SCTPClient::socketFailure(int32, void *, int32 code)
 {
-    // subclasses may override this function, and add code try to reconnect after a delay.
-    ev << "connection broken\n";
-    setStatusString("broken");
-    numBroken++;
-    // reconnect after a delay
-    timeMsg->setKind(MSGKIND_CONNECT);
-    scheduleAt(simulation.getSimTime()+(simtime_t)par("reconnectInterval"), timeMsg);
+   // subclasses may override this function, and add code try to reconnect after a delay.
+   ev << "connection broken\n";
+   setStatusString("broken");
+   numBroken++;
+   // reconnect after a delay
+   timeMsg->setKind(MSGKIND_CONNECT);
+   scheduleAt(simulation.getSimTime()+(simtime_t)par("reconnectInterval"), timeMsg);
 }
 
 void SCTPClient::socketStatusArrived(int32 assocId, void *yourPtr, SCTPStatusInfo *status)
 {
 struct pathStatus ps;
-    SCTPPathStatus::iterator i=sctpPathStatus.find(status->getPathId());
-    if (i!=sctpPathStatus.end())
-    {
-        ps = i->second;
-        ps.active=status->getActive();
-    }
-    else
-    {
-        ps.active = status->getActive();
-        ps.pid = status->getPathId();
-        ps.primaryPath = false;
-        sctpPathStatus[ps.pid]=ps;
-    }
+   SCTPPathStatus::iterator i=sctpPathStatus.find(status->getPathId());
+   if (i!=sctpPathStatus.end())
+   {
+      ps = i->second;
+      ps.active=status->getActive();
+   }
+   else
+   {
+      ps.active = status->getActive();
+      ps.pid = status->getPathId();
+      ps.primaryPath = false;
+      sctpPathStatus[ps.pid]=ps;
+   }
 }
 
 void SCTPClient::setPrimaryPath (const char* str)
 {
 
-    cPacket* cmsg = new cPacket("CMSG-SetPrimary");
-    SCTPPathInfo *pinfo = new SCTPPathInfo();
-    if (strcmp(str,"")!=0)
-    {
-        pinfo->setRemoteAddress(IPvXAddress(str));
-    }
-    else
-    {
-        str = (const char*)par("newPrimary");
-        if (strcmp(str, "")!=0)
-            pinfo->setRemoteAddress(IPvXAddress(str));
-        else
-        {
-            str = (const char*)par("connectAddress");
-            pinfo->setRemoteAddress(IPvXAddress(str));
-        }
-    }
-
-    pinfo->setAssocId(socket.getConnectionId());
-    cmsg->setKind(SCTP_C_PRIMARY);
-    cmsg->setControlInfo(pinfo);
-    socket.sendNotification(cmsg);
+   cPacket* cmsg = new cPacket("CMSG-SetPrimary");
+   SCTPPathInfo *pinfo = new SCTPPathInfo();
+   if (strcmp(str,"")!=0)
+   {
+      pinfo->setRemoteAddress(IPvXAddress(str));
+   }
+   else
+   {
+      str = (const char*)par("newPrimary");
+      if (strcmp(str, "")!=0)
+         pinfo->setRemoteAddress(IPvXAddress(str));
+      else
+      {
+         str = (const char*)par("connectAddress");
+         pinfo->setRemoteAddress(IPvXAddress(str));
+      }
+   }
+
+   pinfo->setAssocId(socket.getConnectionId());
+   cmsg->setKind(SCTP_C_PRIMARY);
+   cmsg->setControlInfo(pinfo);
+   socket.sendNotification(cmsg);
 }
 
 
+void SCTPClient::sendStreamResetNotification()
+{
+   uint32 type;
+
+   type = (uint32)par("streamResetType");
+   if (type >= 6 && type <= 9)
+   {
+      cPacket* cmsg = new cPacket("CMSG-SR");
+      SCTPResetInfo *rinfo = new SCTPResetInfo();
+      rinfo->setAssocId(socket.getConnectionId());
+      rinfo->setRemoteAddr(socket.getRemoteAddr());
+      type = (uint32)par("streamResetType");
+      rinfo->setRequestType((uint16)type);
+      cmsg->setKind(SCTP_C_STREAM_RESET);
+      cmsg->setControlInfo(rinfo);
+      socket.sendNotification(cmsg);
+   }
+}
+
+
+void SCTPClient::msgAbandonedArrived(int32 assocId)
+{
+   chunksAbandoned++;
+}
 
 
 void SCTPClient::sendqueueFullArrived(int32 assocId)
 {
-    sendAllowed = false;
+   sendAllowed = false;
 }
 
 void SCTPClient::sendqueueAbatedArrived(int32 assocId, uint64 buffer)
 {
-    bufferSize = buffer;
-    sendAllowed = true;
-    while ((((!timer && numRequestsToSend>0) || timer) && sendAllowed && bufferSize>0) ||
-                    (((!timer && numRequestsToSend>0) || timer) && sendAllowed && buffer==0))
-    {
-            if (!timer && numRequestsToSend==1)
-                        sendRequest(true);
-                    else
-                        sendRequest(false);
-          if (!timer && (--numRequestsToSend == 0))
-                sendAllowed = false;
-        }
-        if ((!timer && numRequestsToSend == 0) && (simtime_t)par("waitToClose")==0)
-            {
-                sctpEV3<<"socketEstablished:no more packets to send, call shutdown\n";
-                socket.shutdown();
-                if (timeMsg->isScheduled())
-                    cancelEvent(timeMsg);
-                if (finishEndsSimulation) {
-                    endSimulation();
-                }
+   bufferSize = buffer;
+   sendAllowed = true;
+   while ((((!timer && numRequestsToSend>0) || timer) && sendAllowed && bufferSize>0) ||
+            	(((!timer && numRequestsToSend>0) || timer) && sendAllowed && buffer==0))
+	{
+   		if (!timer && numRequestsToSend==1)
+                  sendRequest(true);
+               else
+                  sendRequest(false);
+        if (!timer && (--numRequestsToSend == 0))
+            sendAllowed = false;
+   	}
+   	if ((!timer && numRequestsToSend == 0) && (simtime_t)par("waitToClose")==0)
+         {
+            sctpEV3<<"socketEstablished:no more packets to send, call shutdown\n";
+            socket.shutdown();
+            if (timeMsg->isScheduled())
+               cancelEvent(timeMsg);
+            if (finishEndsSimulation) {
+               endSimulation();
             }
+         }
 }
 
 void SCTPClient::addressAddedArrived(int32 assocId, IPvXAddress remoteAddr)
@@ -535,23 +642,23 @@ void SCTPClient::addressAddedArrived(int32 assocId, IPvXAddress remoteAddr)
 
 void SCTPClient::finish()
 {
-    if (timeMsg->isScheduled())
-        cancelEvent(timeMsg);
-    delete timeMsg;
-    if (stopTimer)
-    {
-        cancelEvent(stopTimer);
-        delete stopTimer;
-    }
-    if (primaryChangeTimer)
-    {
-        cancelEvent(primaryChangeTimer);
-        delete primaryChangeTimer;
-        primaryChangeTimer = NULL;
-    }
-    ev << getFullPath() << ": opened " << numSessions << " sessions\n";
-    ev << getFullPath() << ": sent " << bytesSent << " bytes in " << packetsSent << " packets\n";
-    ev << getFullPath() << ": received " << bytesRcvd << " bytes in " << packetsRcvd << " packets\n";
-    sctpEV3<<"Client finished\n";
+   if (timeMsg->isScheduled())
+      cancelEvent(timeMsg);
+   delete timeMsg;
+   if (stopTimer)
+   {
+      cancelEvent(stopTimer);
+      delete stopTimer;
+   }
+   if (primaryChangeTimer)
+   {
+      cancelEvent(primaryChangeTimer);
+      delete primaryChangeTimer;
+      primaryChangeTimer = NULL;
+   }
+   ev << getFullPath() << ": opened " << numSessions << " sessions\n";
+   ev << getFullPath() << ": sent " << bytesSent << " bytes in " << packetsSent << " packets\n";
+   ev << getFullPath() << ": received " << bytesRcvd << " bytes in " << packetsRcvd << " packets\n";
+   ev << getFullPath() << ": chunks abandoned " << chunksAbandoned << " \n";
+   sctpEV3<<"Client finished\n";
 }
-
diff --git a/src/applications/sctpapp/SCTPClient.h b/src/applications/sctpapp/SCTPClient.h
index 47f4b56..f4a11ab 100644
--- a/src/applications/sctpapp/SCTPClient.h
+++ b/src/applications/sctpapp/SCTPClient.h
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2009 Thomas Dreibholz
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -33,100 +33,106 @@ class SCTPAssociation;
 
 class INET_API SCTPClient : public cSimpleModule, public SCTPSocket::CallbackInterface
 {
-    protected:
-        SCTPSocket socket;
-        SCTPAssociation* assoc;
-        // statistics
-        int32 numSessions;
-        int32 numBroken;
-        uint64 packetsSent;
-        uint64 packetsRcvd;
-        uint64 bytesSent;
-        uint64 echoedBytesSent;
-        uint64 bytesRcvd;
-        uint64 numRequestsToSend; // requests to send in this session
-        uint64 numPacketsToReceive;
-        uint32 numBytes;
-        int64 bufferSize;
-        int32 echoFactor;
-        int32 queueSize;
-        uint32 inStreams;
-        uint32 outStreams;
-        bool ordered;
-        bool sendAllowed;
-        bool timer;
-        bool finishEndsSimulation;
-        cMessage* timeMsg;
-        cMessage* stopTimer;
-        cMessage* primaryChangeTimer;
-        /** Utility: sends a request to the server */
-        void sendRequest(bool last=true);
-    public:
+   protected:
+      SCTPSocket socket;
+      SCTPAssociation* assoc;
+      // statistics
+      int32 numSessions;
+      int32 numBroken;
+      uint64 packetsSent;
+      uint64 packetsRcvd;
+      uint64 bytesSent;
+      uint64 echoedBytesSent;
+      uint64 bytesRcvd;
+      uint64 numRequestsToSend; // requests to send in this session
+      uint64 numPacketsToReceive;
+      uint32 numBytes;
+      int64 bufferSize;
+      int32 echoFactor;
+      int32 queueSize;
+      uint32 inStreams;
+      uint32 outStreams;
+      int32 chunksAbandoned;
+      std::map<uint32,uint32> streamRequestLengthMap;
+      std::map<uint32,uint32> streamRequestRatioMap;
+      std::map<uint32,uint32> streamRequestRatioSendMap;
+      bool ordered;
+      bool sendAllowed;
+      bool timer;
+      bool finishEndsSimulation;
+      cMessage* timeMsg;
+      cMessage* stopTimer;
+      cMessage* primaryChangeTimer;
+      /** Utility: sends a request to the server */
+      void sendRequest(bool last=true);
+   public:
 
-        struct pathStatus {
-            bool active;
-            bool primaryPath;
-            IPvXAddress  pid;
-            };
-        typedef std::map<IPvXAddress,pathStatus> SCTPPathStatus;
-        SCTPPathStatus sctpPathStatus;
-        /**
-        * Initialization.
-        */
-        void initialize();
+      struct pathStatus {
+         bool active;
+         bool primaryPath;
+         IPvXAddress  pid;
+         };
+      typedef std::map<IPvXAddress,pathStatus> SCTPPathStatus;
+      SCTPPathStatus sctpPathStatus;
+      /**
+      * Initialization.
+      */
+      void initialize();
 
-        /**
-        * For self-messages it invokes handleTimer(); messages arriving from SCTP
-        * will get dispatched to the socketXXX() functions.
-        */
-        void handleMessage(cMessage *msg);
+      /**
+      * For self-messages it invokes handleTimer(); messages arriving from SCTP
+      * will get dispatched to the socketXXX() functions.
+      */
+      void handleMessage(cMessage *msg);
 
-        /**
-        * Records basic statistics: numSessions, packetsSent, packetsRcvd,
-        * bytesSent, bytesRcvd. Redefine to record different or more statistics
-        * at the end of the simulation.
-        */
-        void finish();
-        /** @name Utility functions */
-        //@{
-        /** Issues an active OPEN to the address/port given as module parameters */
-        void connect();
-        /** Issues CLOSE command */
-        void close();
-        /** Sends a GenericAppMsg of the given length */
-        //    virtual void sendPacket(int32 numBytes, bool serverClose=false);
-        /** When running under GUI, it displays the given string next to the icon */
-        void setStatusString(const char *s);
-        //@}
-        /** Invoked from handleMessage(). Should be redefined to handle self-messages. */
-        void handleTimer(cMessage *msg);
-        /** @name SCTPSocket::CallbackInterface callback methods */
-        //@{
-        /** Does nothing but update statistics/status. Redefine to perform or schedule first sending. */
-        void socketEstablished(int32 connId, void *yourPtr, uint64 buffer);
-        /**
-        * Does nothing but update statistics/status. Redefine to perform or schedule next sending.
-        * Beware: this funcion deletes the incoming message, which might not be what you want.
-        */
-        void socketDataArrived(int32 connId, void *yourPtr, cPacket *msg, bool urgent);
-        void socketDataNotificationArrived(int32 connId, void *yourPtr, cPacket *msg);
-        /** Since remote SCTP closed, invokes close(). Redefine if you want to do something else. */
-        void socketPeerClosed(int32 connId, void *yourPtr);
-        /** Does nothing but update statistics/status. Redefine if you want to do something else, such as opening a new connection. */
-        void socketClosed(int32 connId, void *yourPtr);
-        /** Does nothing but update statistics/status. Redefine if you want to try reconnecting after a delay. */
-        void socketFailure(int32 connId, void *yourPtr, int32 code);
-        /** Redefine to handle incoming SCTPStatusInfo. */
-        void socketStatusArrived(int32 connId, void *yourPtr, SCTPStatusInfo *status);
-        //@}
-        void setAssociation(SCTPAssociation *_assoc) {assoc = _assoc;};
-        void setPrimaryPath (const char* addr);
-        void sendRequestArrived();
-        void sendQueueRequest();
-        void shutdownReceivedArrived(int32 connId);
-        void sendqueueFullArrived(int32 connId);
-        void sendqueueAbatedArrived(int32 connId, uint64 buffer);
-        void addressAddedArrived(int32 assocId, IPvXAddress remoteAddr);
+      /**
+      * Records basic statistics: numSessions, packetsSent, packetsRcvd,
+      * bytesSent, bytesRcvd. Redefine to record different or more statistics
+      * at the end of the simulation.
+      */
+      void finish();
+      /** @name Utility functions */
+      //@{
+      /** Issues an active OPEN to the address/port given as module parameters */
+      void connect();
+      /** Issues CLOSE command */
+      void close();
+      /** Sends a GenericAppMsg of the given length */
+      //   virtual void sendPacket(int32 numBytes, bool serverClose=false);
+      /** When running under GUI, it displays the given string next to the icon */
+      void setStatusString(const char *s);
+      //@}
+      /** Invoked from handleMessage(). Should be redefined to handle self-messages. */
+      void handleTimer(cMessage *msg);
+      /** @name SCTPSocket::CallbackInterface callback methods */
+      //@{
+      /** Does nothing but update statistics/status. Redefine to perform or schedule first sending. */
+      void socketEstablished(int32 connId, void *yourPtr, uint64 buffer);
+      /**
+      * Does nothing but update statistics/status. Redefine to perform or schedule next sending.
+      * Beware: this funcion deletes the incoming message, which might not be what you want.
+      */
+      void socketDataArrived(int32 connId, void *yourPtr, cPacket *msg, bool urgent);
+      void socketDataNotificationArrived(int32 connId, void *yourPtr, cPacket *msg);
+      /** Since remote SCTP closed, invokes close(). Redefine if you want to do something else. */
+      void socketPeerClosed(int32 connId, void *yourPtr);
+      /** Does nothing but update statistics/status. Redefine if you want to do something else, such as opening a new connection. */
+      void socketClosed(int32 connId, void *yourPtr);
+      /** Does nothing but update statistics/status. Redefine if you want to try reconnecting after a delay. */
+      void socketFailure(int32 connId, void *yourPtr, int32 code);
+      /** Redefine to handle incoming SCTPStatusInfo. */
+      void socketStatusArrived(int32 connId, void *yourPtr, SCTPStatusInfo *status);
+      //@}
+      void msgAbandonedArrived(int32 assocId);
+      void sendStreamResetNotification();
+      void setAssociation(SCTPAssociation *_assoc) {assoc = _assoc;};
+      void setPrimaryPath (const char* addr);
+      void sendRequestArrived();
+      void sendQueueRequest();
+      void shutdownReceivedArrived(int32 connId);
+      void sendqueueFullArrived(int32 connId);
+      void sendqueueAbatedArrived(int32 connId, uint64 buffer);
+      void addressAddedArrived(int32 assocId, IPvXAddress remoteAddr);
 };
 
 #endif
diff --git a/src/applications/sctpapp/SCTPClient.ned b/src/applications/sctpapp/SCTPClient.ned
index 21a2d23..d363223 100644
--- a/src/applications/sctpapp/SCTPClient.ned
+++ b/src/applications/sctpapp/SCTPClient.ned
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2009 Thomas Dreibholz
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -20,29 +20,37 @@ package inet.applications.sctpapp;
 
 simple SCTPClient like SCTPApp
 {
-    parameters:
-        string address = default(""); // may be left empty ("")
-        int port = default(0); // port number to listen on
-        string connectAddress;  // server address (may be symbolic)
-        int connectPort; // port number to connect to
-        double startTime @unit(s) = default(1s); // time first session begins
-        volatile int numRequestsPerSession = default(1);  // number of requests sent per session
-        int numPacketsToReceive = default(0);
-        int requestLength = default(1452); // length of a request (bytes)
-        double thinkTime @unit(s) = default(0s); // time gap between requests
-        double waitToClose @unit(s) = default(0s); //time to wait between last message sent and abort
-        double reconnectInterval @unit(s) = default(0);  // if connection breaks, waits this much before trying to reconnect
-        int inboundStreams = default(17);
-        int outboundStreams = default(1);
-        int echoFactor = default(0);
-        bool ordered = default(true);
-        int queueSize = default(0);
-        double stopTime @unit(s) = default(0s);
-        double primaryTime @unit(s) = default(0s);
-        string newPrimary = default("");
-        bool finishEndsSimulation = default(false);
-    gates:
-        input sctpIn;
-        output sctpOut;
+   parameters:
+      string address = default(""); // may be left empty ("")
+      int port = default(0); // port number to listen on
+      string connectAddress;  // server address (may be symbolic)
+      int connectPort; // port number to connect to
+      double startTime @unit(s) = default(1s); // time first session begins
+      volatile int numRequestsPerSession = default(1);  // number of requests sent per session
+      int numPacketsToReceive = default(0);
+      int requestLength = default(1452); // length of a request (bytes)
+      double thinkTime @unit(s) = default(0s); // time gap between requests
+      double waitToClose @unit(s) = default(0s); //time to wait between last message sent and abort
+      double reconnectInterval @unit(s) = default(0);  // if connection breaks, waits this much before trying to reconnect
+      int inboundStreams = default(17);
+      int outboundStreams = default(1);
+      int echoFactor = default(0);
+      bool ordered = default(true);
+      int queueSize = default(0);
+      int prMethod = default(0);  //0=NONE, 1=PR_TTL, 2=PR_RTX, 3=PR_PRIO, 4=PR_STRRST
+      bool streamReset = default(false);
+      double streamRequestTime @unit(s) = default(0s);
+      int streamResetType = default(5);  //NO_RESET=5, RESET_OUTGOING=6, RESET_INCOMING=7, RESET_BOTH=8, SSN_TSN=9
+      bool streamAnswer = default(false);
+      double prValue = default(0); //for PR-SCTP
+      double stopTime @unit(s) = default(0s);
+      double primaryTime @unit(s) = default(0s);
+      string newPrimary = default("");
+      string streamRequestLengths = default("");
+      string streamRequestRatio = default("");
+      string streamPriorities = default("");
+      bool finishEndsSimulation = default(false);
+   gates:
+      input sctpIn;
+      output sctpOut;
 }
-
diff --git a/src/applications/sctpapp/SCTPNatPeer.cc b/src/applications/sctpapp/SCTPNatPeer.cc
new file mode 100644
index 0000000..8ef8635
--- /dev/null
+++ b/src/applications/sctpapp/SCTPNatPeer.cc
@@ -0,0 +1,944 @@
+//
+// Copyright 2005 Irene Ruengeler
+//
+// This library is free software, you can redistribute it and/or modify
+// it under  the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation;
+// either version 2 of the License, or any later version.
+// The library 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 Lesser General Public License for more details.
+//
+
+#include "SCTPNatPeer.h"
+#include "SCTPSocket.h"
+#include "SCTPCommand_m.h"
+#include "SCTPMessage_m.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include "SCTPAssociation.h"
+#include "IPAddressResolver.h"
+
+#define MSGKIND_CONNECT  0
+#define MSGKIND_SEND     1
+#define MSGKIND_ABORT    2
+#define MSGKIND_PRIMARY  3
+#define MSGKIND_RESET    4
+#define MSGKIND_STOP    5
+
+Define_Module(SCTPNatPeer);
+
+void SCTPNatPeer::initialize()
+{
+   char * token;
+
+   numSessions = packetsSent = packetsRcvd = bytesSent = notifications = 0;
+   WATCH(numSessions);
+   WATCH(packetsSent);
+   WATCH(packetsRcvd);
+   WATCH(bytesSent);
+   //WATCH(rcvdBytesPerAssoc);
+   WATCH(numRequestsToSend);
+   // parameters
+   const char* address = par("address");
+   token = strtok((char*)address,",");
+   while (token != NULL)
+   {
+      //sctpEV3<<"address="<<token<<"\n";
+      localAddressList.push_back(IPvXAddress(token));
+      token = strtok(NULL, ",");
+   }
+   int32 port = par("port");
+   echoFactor = par("echoFactor");
+   delay = par("echoDelay");
+   outboundStreams = par("outboundStreams");
+   ordered = (bool)par("ordered");
+   queueSize = par("queueSize");
+   lastStream = 0;
+   timeoutMsg = new cMessage("SrvAppTimer");
+   SCTPSocket* socket = new SCTPSocket();
+   socket->setOutputGate(gate("sctpOut"));
+   socket->setOutboundStreams(outboundStreams);
+   if (strcmp(address,"")==0)
+   {
+      socket->bind(port);
+      clientSocket.bind(port);
+   }
+   else
+   {
+      socket->bindx(localAddressList, port);
+      clientSocket.bindx(localAddressList, port);
+   }
+   socket->listen(true,(bool)par("streamReset"), par("numPacketsToSendPerClient"));
+   sctpEV3<<"SCTPNatPeer::initialized listen port="<<port<<"\n";
+   clientSocket.setCallbackObject(this);
+   clientSocket.setOutputGate(gate("sctpOut"));
+   rendezvous = (bool)par("rendezvous");
+   if ((simtime_t)par("startTime")>0)
+   {
+      cMessage *msg = new cMessage("ConnectTimer");
+      msg->setKind(MSGKIND_CONNECT);
+      scheduleAt((simtime_t)par("startTime"), msg);
+   }
+   schedule = false;
+   shutdownReceived = false;
+   sendAllowed = true;
+}
+
+void SCTPNatPeer::sendOrSchedule(cPacket *msg)
+{
+   if (delay==0)
+   {
+      send(msg, "sctpOut");
+   }
+   else
+   {
+      scheduleAt(simulation.getSimTime()+delay, msg);
+   }
+}
+
+void SCTPNatPeer::generateAndSend(SCTPConnectInfo *connectInfo)
+{
+uint32 numBytes;
+   //sctpEV3 << "SCTPNatPeer:generateAndSend \n";
+
+   cPacket* cmsg = new cPacket("CMSG");
+   SCTPSimpleMessage* msg=new SCTPSimpleMessage("Server");
+   numBytes=(int64)(long)par("requestLength");
+   msg->setDataArraySize(numBytes);
+   for (uint32 i=0; i<numBytes; i++)
+   {
+      msg->setData(i, 's');
+   }
+   msg->setDataLen(numBytes);
+   msg->setEncaps(false);
+   msg->setBitLength(numBytes * 8);
+   cmsg->encapsulate(msg);
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(serverAssocId);
+   if (ordered)
+      cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
+   else
+      cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
+   lastStream=(lastStream+1)%outboundStreams;
+   cmd->setSid(lastStream);
+   cmd->setPrValue((double)par("prValue"));
+   cmd->setPrMethod((int32)par("prMethod"));
+   cmd->setLast(true);
+   cmsg->setKind(SCTP_C_SEND);
+   cmsg->setControlInfo(cmd);
+   packetsSent++;
+   bytesSent+=msg->getBitLength()/8;
+   sendOrSchedule(cmsg);
+}
+
+void SCTPNatPeer::connectx(AddressVector connectAddressList, int32 connectPort)
+{
+   //const char *connectAddress = par("connectAddress");
+   //int32 connectPort = par("connectPort");
+   uint32 outStreams = par("outboundStreams");
+   clientSocket.setOutboundStreams(outStreams);
+
+   sctpEV3 << "issuing OPEN command\n";
+   //setStatusString("connecting");
+   //socket.connect(IPAddressResolver().resolve(connectAddress), connectPort);
+   sctpEV3<<"Assoc "<<clientSocket.getConnectionId()<<"::connect to  port "<<connectPort<<"\n";
+   bool streamReset = par("streamReset");
+   clientSocket.connectx(connectAddressList, connectPort, streamReset, (int32)par("prMethod"), (uint32)par("numRequestsPerSession"));
+   numSessions++;
+
+   if (!streamReset)
+      streamReset = false;
+   else if (streamReset == true)
+   {
+      cMessage* cmsg = new cMessage("StreamReset");
+      cmsg->setKind(MSGKIND_RESET);
+      sctpEV3<<"StreamReset Timer scheduled at "<<simulation.getSimTime()<<"\n";
+      scheduleAt(simulation.getSimTime()+(double)par("streamRequestTime"), cmsg);
+   }
+   uint32 streamNum = 0;
+   cStringTokenizer tokenizer(par("streamPriorities").stringValue());
+   while (tokenizer.hasMoreTokens())
+   {
+      const char *token = tokenizer.nextToken();
+      clientSocket.setStreamPriority(streamNum, (uint32) atoi(token));
+
+      streamNum++;
+   }
+}
+
+void SCTPNatPeer::connect(IPvXAddress connectAddress, int32 connectPort)
+{
+   //const char *connectAddress = par("connectAddress");
+   //int32 connectPort = par("connectPort");
+   uint32 outStreams = par("outboundStreams");
+   clientSocket.setOutboundStreams(outStreams);
+
+   sctpEV3 << "issuing OPEN command\n";
+   //setStatusString("connecting");
+   //socket.connect(IPAddressResolver().resolve(connectAddress), connectPort);
+   sctpEV3<<"Assoc "<<clientSocket.getConnectionId()<<"::connect to address "<<connectAddress<<", port "<<connectPort<<"\n";
+   bool streamReset = par("streamReset");
+   clientSocket.connect(connectAddress, connectPort, streamReset, (int32)par("prMethod"), (uint32)par("numRequestsPerSession"));
+   numSessions++;
+
+   if (!streamReset)
+      streamReset = false;
+   else if (streamReset == true)
+   {
+      cMessage* cmsg = new cMessage("StreamReset");
+      cmsg->setKind(MSGKIND_RESET);
+      sctpEV3<<"StreamReset Timer scheduled at "<<simulation.getSimTime()<<"\n";
+      scheduleAt(simulation.getSimTime()+(double)par("streamRequestTime"), cmsg);
+   }
+   uint32 streamNum = 0;
+   cStringTokenizer tokenizer(par("streamPriorities").stringValue());
+   while (tokenizer.hasMoreTokens())
+   {
+      const char *token = tokenizer.nextToken();
+      clientSocket.setStreamPriority(streamNum, (uint32) atoi(token));
+
+      streamNum++;
+   }
+}
+
+void SCTPNatPeer::handleMessage(cMessage *msg)
+{
+   int32 id;
+
+
+   ////sctpEV3<<"SCTPNatPeer::handleMessage kind="<<SCTPAssociation::indicationName(msg->getKind())<<" ("<<msg->getKind()<<")\n";
+
+   if (msg->isSelfMessage())
+   {
+
+      handleTimer(msg);
+   }
+   switch (msg->getKind())
+   {
+      case SCTP_I_ADDRESS_ADDED:
+         if (rendezvous)
+            clientSocket.processMessage(PK(msg));
+         else
+            delete msg;
+      break;
+      case SCTP_I_PEER_CLOSED:
+      case SCTP_I_ABORT:
+      {
+         if (rendezvous)
+            clientSocket.processMessage(PK(msg));
+         else
+         {
+            SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->getControlInfo()->dup());
+            cPacket* cmsg = new cPacket("Notification");
+            SCTPSendCommand *cmd = new SCTPSendCommand();
+            id = ind->getAssocId();
+            cmd->setAssocId(id);
+            cmd->setSid(ind->getSid());
+            cmd->setNumMsgs(ind->getNumMsgs());
+            cmsg->setControlInfo(cmd);
+            delete ind;
+            delete msg;
+            cmsg->setKind(SCTP_C_ABORT);
+            sendOrSchedule(cmsg);
+         }
+
+         break;
+      }
+      case SCTP_I_ESTABLISHED:
+      {
+         if (clientSocket.getState()==SCTPSocket::CONNECTING)
+            clientSocket.processMessage(PK(msg));
+         else
+         {
+            int32 count=0;
+            SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->removeControlInfo());
+            numSessions++;
+            serverAssocId = connectInfo->getAssocId();
+            id = serverAssocId;
+            outboundStreams = connectInfo->getOutboundStreams();
+            rcvdPacketsPerAssoc[serverAssocId]= (int64) (long)par("numPacketsToReceivePerClient");
+            sentPacketsPerAssoc[serverAssocId]= (int64) (long)par("numPacketsToSendPerClient");
+            char text[30];
+            sprintf(text, "App: Received Bytes of assoc %d",serverAssocId);
+            bytesPerAssoc[serverAssocId] = new cOutVector(text);
+            rcvdBytesPerAssoc[serverAssocId]= 0;
+            sprintf(text, "App: EndToEndDelay of assoc %d",serverAssocId);
+            endToEndDelay[serverAssocId] = new cOutVector(text);
+            sprintf(text, "Hist: EndToEndDelay of assoc %d",serverAssocId);
+            histEndToEndDelay[serverAssocId] = new cDoubleHistogram(text);
+
+            //delete connectInfo;
+            delete msg;
+            if ((int64)(long) par("numPacketsToSendPerClient") > 0)
+            {
+               SentPacketsPerAssoc::iterator i=sentPacketsPerAssoc.find(serverAssocId);
+               numRequestsToSend = i->second;
+               if ((simtime_t)par("thinkTime") > 0)
+               {
+                  generateAndSend(connectInfo);
+                  timeoutMsg->setKind(SCTP_C_SEND);
+                  scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
+                  numRequestsToSend--;
+                  i->second = numRequestsToSend;
+               }
+               else
+               {
+                  if (queueSize==0)
+                  {
+                     while (numRequestsToSend > 0)
+                     {
+                        generateAndSend(connectInfo);
+                        numRequestsToSend--;
+                        i->second = numRequestsToSend;
+                     }
+                  }
+                  else if (queueSize>0)
+                  {
+                     while (numRequestsToSend > 0 && count++ < queueSize*2)
+                     {
+                        generateAndSend(connectInfo);
+                        numRequestsToSend--;
+                        i->second = numRequestsToSend;
+                     }
+
+                     cPacket* cmsg = new cPacket("Queue");
+                     SCTPInfo* qinfo = new SCTPInfo();
+                     qinfo->setText(queueSize);
+                     cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
+                     qinfo->setAssocId(id);
+                     cmsg->setControlInfo(qinfo);
+                     sendOrSchedule(cmsg);
+                  }
+
+                  sctpEV3<<"!!!!!!!!!!!!!!!All data sent from Peer !!!!!!!!!!\n";
+
+                  RcvdPacketsPerAssoc::iterator j=rcvdPacketsPerAssoc.find(serverAssocId);
+                  if (j->second == 0 && (simtime_t)par("waitToClose")>0)
+                  {
+                     char as[5];
+                     sprintf(as, "%d",serverAssocId);
+                     cPacket* abortMsg = new cPacket(as);
+                     abortMsg->setKind(SCTP_I_ABORT);
+                     scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), abortMsg);
+                  }
+                  else
+                  {
+                     sctpEV3<<"no more packets to send, call shutdown for assoc "<<serverAssocId<<"\n";
+                     cPacket* cmsg = new cPacket("ShutdownRequest");
+                     //SCTPInfo* qinfo = new SCTPInfo();
+                     SCTPCommand* cmd = new SCTPCommand();
+                     cmsg->setKind(SCTP_C_SHUTDOWN);
+                     cmd->setAssocId(serverAssocId);
+                     //qinfo->setAssocId(id);
+                     //cmsg->setControlInfo(qinfo);
+                     cmsg->setControlInfo(cmd);
+                     sendOrSchedule(cmsg);
+                  }
+               }
+            }
+         }
+         break;
+      }
+      case SCTP_I_DATA_NOTIFICATION:
+      {
+         notifications++;
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         cPacket* cmsg = new cPacket("Notification");
+         SCTPSendCommand *cmd = new SCTPSendCommand();
+         id = ind->getAssocId();
+         cmd->setAssocId(id);
+         cmd->setSid(ind->getSid());
+         cmd->setNumMsgs(ind->getNumMsgs());
+         cmsg->setKind(SCTP_C_RECEIVE);
+         cmsg->setControlInfo(cmd);
+         delete ind;
+         delete msg;
+         if (!cmsg->isScheduled() && schedule==false)
+         {
+            scheduleAt(simulation.getSimTime()+(simtime_t)par("delayFirstRead"), cmsg);
+         }
+         else if (schedule==true)
+            sendOrSchedule(cmsg);
+         break;
+      }
+      case SCTP_I_DATA:
+      {
+         //bytesRcvd += msg->getBitLength()/8;
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->getControlInfo());
+         id = ind->getAssocId();
+         if (rendezvous)
+         {
+            SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg);
+            NatMessage* nat = check_and_cast <NatMessage*>(smsg->decapsulate());
+            if (nat->getMulti())
+            {
+               peerAddressList.push_back(nat->getPeer2Addresses(0));
+               peerAddressList.push_back(nat->getPeer2Addresses(1));
+            }
+            else
+            {
+               peerAddress = nat->getPeer2Addresses(0);
+            }
+               peerPort = nat->getPortPeer2();
+            delete nat;
+            delete smsg;
+
+         }
+         else
+         {
+            RcvdBytesPerAssoc::iterator j=rcvdBytesPerAssoc.find(id);
+            if (j==rcvdBytesPerAssoc.end() && (clientSocket.getState()==SCTPSocket::CONNECTED))
+               clientSocket.processMessage(PK(msg));
+            else
+            {
+               j->second+= PK(msg)->getBitLength()/8;
+               BytesPerAssoc::iterator k=bytesPerAssoc.find(id);
+               k->second->record(j->second);
+               packetsRcvd++;
+               if (echoFactor==0)
+               {
+                  if ((int64)(long)par("numPacketsToReceivePerClient")>0)
+                  {
+                     RcvdPacketsPerAssoc::iterator i=rcvdPacketsPerAssoc.find(id);
+                     i->second--;
+                     SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg);
+                     EndToEndDelay::iterator j=endToEndDelay.find(id);
+                     j->second->record(simulation.getSimTime()-smsg->getCreationTime());
+                     HistEndToEndDelay::iterator k=histEndToEndDelay.find(id);
+                     k->second->collect(simulation.getSimTime()-smsg->getCreationTime());
+                     //recordScalar("EndToEndDelay",simulation.getSimTime()-smsg->getCreationTime());
+
+                     if (i->second == 0)
+                     {
+                        cPacket* cmsg = new cPacket("Request");
+                        SCTPInfo* qinfo = new SCTPInfo();
+                        cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+                        qinfo->setAssocId(id);
+                        cmsg->setControlInfo(qinfo);
+                        sendOrSchedule(cmsg);
+                     }
+                  }
+                  delete msg;
+               }
+               else
+               {
+                  //FIXME: echoFactor ?
+                  // reverse direction, modify length, and send it back
+                  //SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->getControlInfo());
+                  SCTPSendCommand *cmd = new SCTPSendCommand();
+                  cmd->setAssocId(id);
+
+                  //sctpEV3<<msg->getBitLength()/8<<" bytes received\n";
+
+
+                  SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
+                  EndToEndDelay::iterator j=endToEndDelay.find(id);
+                  j->second->record(simulation.getSimTime()-smsg->getCreationTime());
+                  HistEndToEndDelay::iterator k=histEndToEndDelay.find(id);
+                  k->second->collect(simulation.getSimTime()-smsg->getCreationTime());
+                  //recordScalar("EndToEndDelay",simulation.getSimTime()-smsg->getCreationTime());
+                  /*int32 len = smsg->getDataArraySize();
+                  for (int32 i=0; i<len; i++)
+                     str[i] = smsg->getData(i);*/
+                  cPacket* cmsg = new cPacket("SVData");
+                  bytesSent+=smsg->getBitLength()/8;
+                  cmd->setSendUnordered(cmd->getSendUnordered());
+                  lastStream=(lastStream+1)%outboundStreams;
+                  cmd->setPrValue(0);
+                  cmd->setSid(lastStream);
+                  cmd->setLast(true);
+                  cmsg->encapsulate(smsg);
+                  cmsg->setKind(SCTP_C_SEND);
+                  cmsg->setControlInfo(cmd);
+                  packetsSent++;
+                  delete msg;
+                  sendOrSchedule(cmsg);
+               }
+            }
+         }
+         break;
+      }
+      case SCTP_I_SHUTDOWN_RECEIVED:
+      {
+         SCTPCommand *command = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         id = command->getAssocId();
+         sctpEV3<<"peer: SCTP_I_SHUTDOWN_RECEIVED for assoc "<<id<<"\n";
+         RcvdPacketsPerAssoc::iterator i=rcvdPacketsPerAssoc.find(id);
+         if (i==rcvdPacketsPerAssoc.end()&& (clientSocket.getState()==SCTPSocket::CONNECTED))
+            clientSocket.processMessage(PK(msg));
+         else
+         {
+            if (i->second == 0)
+            {
+               cPacket* cmsg = new cPacket("Request");
+               SCTPInfo* qinfo = new SCTPInfo();
+               cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+               qinfo->setAssocId(id);
+               cmsg->setControlInfo(qinfo);
+               sendOrSchedule(cmsg);
+            }
+
+            shutdownReceived = true;
+            delete msg;
+         }
+         delete command;
+         //delete msg;
+      }
+      case SCTP_I_SEND_STREAMS_RESETTED:
+      case SCTP_I_RCV_STREAMS_RESETTED:
+      {
+         ev<< "Streams have been resetted\n";
+         break;
+      }
+      case SCTP_I_CLOSED:
+         delete msg;
+      break;
+   }
+
+   if (ev.isGUI())
+   {
+      char buf[32];
+      RcvdBytesPerAssoc::iterator l=rcvdBytesPerAssoc.find(id);
+      sprintf(buf, "rcvd: %lld bytes\nsent: %lld bytes", (long long int)l->second, (long long int)bytesSent);
+      getDisplayString().setTagArg("t",0,buf);
+   }
+}
+
+void SCTPNatPeer::handleTimer(cMessage *msg)
+{
+   cPacket* cmsg;
+   SCTPCommand* cmd;
+   int32 id;
+
+
+   sctpEV3<<"SCTPNatPeer::handleTimer\n";
+
+   SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->getControlInfo());
+   switch (msg->getKind())
+   {
+      case MSGKIND_CONNECT:
+         sctpEV3 << "starting session call connect\n";
+         connect(IPAddressResolver().resolve(par("connectAddress"), 1),par("connectPort"));
+         delete msg;
+         break;
+      case SCTP_C_SEND:
+
+         //sctpEV3<<"SCTPNatPeer:MSGKIND_SEND\n";
+
+         if (numRequestsToSend>0)
+         {
+            generateAndSend(connectInfo);
+            if ((simtime_t)par("thinkTime") > 0)
+               scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
+            numRequestsToSend--;
+         }
+         break;
+      case SCTP_I_ABORT:
+
+         //sctpEV3<<"SCTPNatPeer:MsgKIND_ABORT for assoc "<<atoi(msg->getName())<<"\n";
+
+         cmsg = new cPacket("CLOSE", SCTP_C_CLOSE);
+         cmd = new SCTPCommand();
+         id = atoi(msg->getName());
+         cmd->setAssocId(id);
+         cmsg->setControlInfo(cmd);
+         sendOrSchedule(PK(cmsg));
+         break;
+      case SCTP_C_RECEIVE:
+
+         //sctpEV3<<"SCTPNatPeer:SCTP_C_RECEIVE\n";
+         schedule = true;
+         sendOrSchedule(PK(msg));
+         break;
+      default:
+
+         //sctpEV3<<"MsgKind ="<<msg->getKind()<<" unknown\n";
+
+         break;
+   }
+   delete connectInfo;
+}
+
+void SCTPNatPeer::socketDataNotificationArrived(int32 connId, void *ptr, cPacket *msg)
+{
+   SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+   cPacket* cmsg = new cPacket("CMSG");
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(ind->getAssocId());
+   cmd->setSid(ind->getSid());
+   cmd->setNumMsgs(ind->getNumMsgs());
+   cmsg->setKind(SCTP_C_RECEIVE);
+   cmsg->setControlInfo(cmd);
+   delete ind;
+   clientSocket.sendNotification(cmsg);
+}
+
+
+void SCTPNatPeer::socketPeerClosed(int32, void *)
+{
+   // close the connection (if not already closed)
+   if (clientSocket.getState()==SCTPSocket::PEER_CLOSED)
+   {
+      ev << "remote SCTP closed, closing here as well\n";
+      setStatusString("closing");
+      clientSocket.close();
+      if (rendezvous)
+      {
+         if ((bool)par("multi"))
+            connectx(peerAddressList, peerPort);
+         else
+            connect(peerAddress,peerPort);
+         rendezvous = false;
+      }
+   }
+}
+
+void SCTPNatPeer::socketClosed(int32, void *)
+{
+   // *redefine* to start another session etc.
+
+   ev << "connection closed\n";
+   setStatusString("closed");
+   if (rendezvous)
+   {
+      if ((bool)par("multi"))
+         connectx(peerAddressList, peerPort);
+      else
+         connect(peerAddress,peerPort);
+      rendezvous = false;
+   }
+}
+
+void SCTPNatPeer::socketFailure(int32, void *, int32 code)
+{
+   // subclasses may override this function, and add code try to reconnect after a delay.
+   ev << "connection broken\n";
+   setStatusString("broken");
+
+   //numBroken++;
+
+   // reconnect after a delay
+   timeMsg->setKind(MSGKIND_CONNECT);
+   scheduleAt(simulation.getSimTime()+(simtime_t)par("reconnectInterval"), timeMsg);
+}
+
+void SCTPNatPeer::socketStatusArrived(int32 assocId, void *yourPtr, SCTPStatusInfo *status)
+{
+struct pathStatus ps;
+   SCTPPathStatus::iterator i=sctpPathStatus.find(status->getPathId());
+   if (i!=sctpPathStatus.end())
+   {
+      ps = i->second;
+      ps.active=status->getActive();
+   }
+   else
+   {
+      ps.active = status->getActive();
+      //ps.pid = status->pathId();  FIXME
+      ps.primaryPath = false;
+      sctpPathStatus[ps.pid]=ps;
+   }
+}
+
+void SCTPNatPeer::setStatusString(const char *s)
+{
+   if (ev.isGUI()) getDisplayString().setTagArg("t", 0, s);
+}
+
+void SCTPNatPeer::sendRequest(bool last)
+{
+   sctpEV3 << "sending request, " << numRequestsToSend-1 << " more to go\n";
+   uint32 i;
+   int64 numBytes =(int64)(long) par("requestLength");
+   if (numBytes < 1)
+      numBytes=1;
+
+   sctpEV3 << "SCTPClient: sending " << numBytes << " data bytes\n";
+
+   cPacket* cmsg = new cPacket("AppData");
+   SCTPSimpleMessage* msg=new SCTPSimpleMessage("data");
+
+   msg->setDataArraySize(numBytes);
+   for (i=0; i<numBytes; i++)
+   {
+      msg->setData(i, 'a');
+   }
+   msg->setDataLen(numBytes);
+   msg->setEncaps(false);
+   msg->setBitLength(numBytes * 8);
+   msg->setCreationTime(simulation.getSimTime());
+   cmsg->encapsulate(msg);
+   if (ordered)
+      cmsg->setKind(SCTP_C_SEND_ORDERED);
+   else
+      cmsg->setKind(SCTP_C_SEND_UNORDERED);
+   // send SCTPMessage with SCTPSimpleMessage enclosed
+   clientSocket.send(cmsg);
+   bytesSent+=numBytes;
+}
+
+
+void SCTPNatPeer::socketEstablished(int32, void *)
+{
+   int32 count = 0;
+    // *redefine* to perform or schedule first sending
+   ev<<"SCTPClient: connected\n";
+   setStatusString("connected");
+   if (rendezvous)
+   {
+      NatMessage* msg = new NatMessage("Rendezvous");
+      msg->setKind(SCTP_C_NAT_INFO);
+      msg->setMulti((bool)par("multi"));
+      msg->setPeer1(par("ownName"));
+      msg->setPeer1AddressesArraySize(1);
+      msg->setPeer1Addresses(0,IPvXAddress("0.0.0.0"));
+      msg->setPortPeer1(par("port"));
+      msg->setPeer2(par("peerName"));
+      msg->setPeer2AddressesArraySize(1);
+      msg->setPeer2Addresses(0,IPvXAddress("0.0.0.0"));
+      msg->setPortPeer2(0);
+      cPacket* cmsg = new cPacket(msg->getName());
+      SCTPSimpleMessage* smsg=new SCTPSimpleMessage("nat_data");
+      smsg->setEncaps(true);
+      smsg->encapsulate(msg);
+      smsg->setCreationTime(simulation.getSimTime());
+      smsg->setBitLength(16*8);
+      cmsg->encapsulate(smsg);
+      clientSocket.send(cmsg);
+
+      if ((bool)par("multi"))
+      {
+         cPacket* cmesg = new cPacket("Notification");
+         SCTPCommand *cmd = new SCTPCommand();
+         cmd->setAssocId(clientSocket.getConnectionId());
+         cmesg->setControlInfo(cmd);
+         cmesg->setKind(SCTP_C_SEND_ASCONF);
+         clientSocket.sendNotification(cmesg);
+      }
+   }
+   else
+   {
+   // determine number of requests in this session
+   numRequestsToSend = (int64) (long)par("numRequestsPerSession");
+   numPacketsToReceive = (int64)(long) par("numPacketsToReceive");
+   if (numRequestsToSend<1)
+      numRequestsToSend = 0;
+   // perform first request (next one will be sent when reply arrives)
+   if (numRequestsToSend>0)
+   {
+      if ((simtime_t)par("thinkTime") > 0)
+      {
+         if (sendAllowed)
+         {
+            sendRequest();
+            numRequestsToSend--;
+         }
+         timeMsg->setKind(MSGKIND_SEND);
+         scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeMsg);
+
+      }
+      else
+      {
+         if (queueSize>0)
+         {
+            while (numRequestsToSend > 0 && count++ < queueSize*2 && sendAllowed)
+            {
+               if (count == queueSize*2)
+                  sendRequest();
+               else
+                  sendRequest(false);
+               numRequestsToSend--;
+            }
+            if (numRequestsToSend>0 && sendAllowed)
+               sendQueueRequest();
+         }
+         else
+         {
+            while (numRequestsToSend > 0 && sendAllowed)
+            {
+               sendRequest();
+               numRequestsToSend--;
+            }
+         }
+
+         if (numPacketsToReceive == 0 && (simtime_t)par("waitToClose")>0)
+         {
+            timeMsg->setKind(MSGKIND_ABORT);
+            scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), timeMsg);
+         }
+         if (numRequestsToSend == 0 && (simtime_t)par("waitToClose")==0)
+         {
+            sctpEV3<<"socketEstablished:no more packets to send, call shutdown\n";
+            clientSocket.shutdown();
+         }
+      }
+   }
+   }
+}
+
+void SCTPNatPeer::sendQueueRequest()
+{
+   cPacket* cmsg = new cPacket("Queue");
+   SCTPInfo* qinfo = new SCTPInfo();
+   qinfo->setText(queueSize);
+   cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
+   qinfo->setAssocId(clientSocket.getConnectionId());
+   cmsg->setControlInfo(qinfo);
+   clientSocket.sendRequest(cmsg);
+
+}
+
+
+void SCTPNatPeer::sendRequestArrived()
+{
+int32 count = 0;
+
+   sctpEV3<<"sendRequestArrived numRequestsToSend="<<numRequestsToSend<<"\n";
+   while (numRequestsToSend > 0 && count++ < queueSize && sendAllowed)
+   {
+      numRequestsToSend--;
+      if (count == queueSize || numRequestsToSend==0)
+         sendRequest();
+      else
+         sendRequest(false);
+
+      if (numRequestsToSend == 0)
+      {
+         sctpEV3<<"no more packets to send, call shutdown\n";
+         clientSocket.shutdown();
+      }
+   }
+
+
+}
+
+void SCTPNatPeer::socketDataArrived(int32, void *, cPacket *msg, bool)
+{
+   // *redefine* to perform or schedule next sending
+   packetsRcvd++;
+
+   sctpEV3<<"Client received packet Nr "<<packetsRcvd<<" from SCTP\n";
+   //SCTPSendCommand* ind = check_and_cast<SCTPSendCommand*>(msg->removegetControlInfo());
+   SCTPCommand* ind = check_and_cast<SCTPCommand*>(msg->getControlInfo());
+   bytesRcvd+=msg->getBitLength()/8;
+
+   if (echoFactor > 0)
+   {
+      //FIXME: echoFactor ?
+      // reverse direction, modify length, and send it back
+
+      //sctpEV3<<msg->getBitLength()/8<<" bytes received\n";
+
+      SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
+      cPacket* cmsg = new cPacket("SVData");
+      echoedBytesSent+=smsg->getBitLength()/8;
+      cmsg->encapsulate(smsg);
+      if (ind->getSendUnordered())
+         cmsg->setKind(SCTP_C_SEND_UNORDERED);
+      else
+         cmsg->setKind(SCTP_C_SEND_ORDERED);
+      packetsSent++;
+      delete msg;
+      clientSocket.send(cmsg, 0,0,1);
+      //socket.send(cmsg);
+   }
+   if ((int64)(long)par("numPacketsToReceive")>0)
+   {
+      numPacketsToReceive--;
+      if (numPacketsToReceive == 0)
+      {
+         //setStatusString("closing");
+         //clientSocket.close();
+         sctpEV3<<"Peer: all packets received\n";
+      }
+   }
+}
+
+
+
+void SCTPNatPeer::shutdownReceivedArrived(int32 connId)
+{
+   if (numRequestsToSend==0 || rendezvous)
+   {
+      cPacket* cmsg = new cPacket("Request");
+      SCTPInfo* qinfo = new SCTPInfo();
+      cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+      qinfo->setAssocId(connId);
+      cmsg->setControlInfo(qinfo);
+      clientSocket.sendNotification(cmsg);
+   }
+}
+
+void SCTPNatPeer::msgAbandonedArrived(int32 assocId)
+{
+   chunksAbandoned++;
+}
+
+void SCTPNatPeer::sendqueueFullArrived(int32 assocId)
+{
+   sendAllowed = false;
+}
+
+void SCTPNatPeer::addressAddedArrived(int32 assocId, IPvXAddress localAddr, IPvXAddress remoteAddr)
+{
+   sctpEV3<<getFullPath()<<": addressAddedArrived for remoteAddr "<<remoteAddr<<"\n";
+   localAddressList.push_back(localAddr);
+   clientSocket.addAddress(localAddr);
+   if (rendezvous)
+   {
+      NatMessage* msg = new NatMessage("Rendezvous");
+      msg->setKind(SCTP_C_NAT_INFO);
+      msg->setMulti((bool)par("multi"));
+      msg->setPeer1(par("ownName"));
+      msg->setPeer1AddressesArraySize(2);
+      msg->setPeer1Addresses(0,IPvXAddress("0.0.0.0"));
+      msg->setPeer1Addresses(1,IPvXAddress("0.0.0.0"));
+      msg->setPortPeer1(par("port"));
+      msg->setPeer2(par("peerName"));
+      msg->setPeer2AddressesArraySize(2);
+      msg->setPeer2Addresses(0,IPvXAddress("0.0.0.0"));
+      msg->setPeer2Addresses(1,IPvXAddress("0.0.0.0"));
+      msg->setPortPeer2(0);
+      cPacket* cmsg = new cPacket(msg->getName());
+      SCTPSimpleMessage* smsg=new SCTPSimpleMessage("nat_data");
+      smsg->setEncaps(true);
+      smsg->encapsulate(msg);
+      smsg->setCreationTime(simulation.getSimTime());
+      smsg->setBitLength(16*8);
+      cmsg->encapsulate(smsg);
+      clientSocket.send(cmsg, false, true);
+   }
+}
+
+void SCTPNatPeer::finish()
+{
+   delete timeoutMsg;
+   ev << getFullPath() << ": opened " << numSessions << " sessions\n";
+   ev << getFullPath() << ": sent " << bytesSent << " bytes in " << packetsSent << " packets\n";
+   for (RcvdBytesPerAssoc::iterator l=rcvdBytesPerAssoc.begin(); l!=rcvdBytesPerAssoc.end(); l++)
+   {
+      ev << getFullPath() << ": received " << l->second << " bytes in assoc " << l->first<< "\n";
+   }
+   ev << getFullPath() << "Over all " << packetsRcvd << " packets received\n ";
+   ev << getFullPath() << "Over all " << notifications << " notifications received\n ";
+   for (BytesPerAssoc::iterator j = bytesPerAssoc.begin(); j!= bytesPerAssoc.end(); j++)
+   {
+      delete j->second;
+      bytesPerAssoc.erase(j);
+   }
+   for (EndToEndDelay::iterator k = endToEndDelay.begin(); k!= endToEndDelay.end(); k++)
+   {
+      delete k->second;
+      endToEndDelay.erase(k);
+   }
+   for (HistEndToEndDelay::iterator l = histEndToEndDelay.begin(); l!= histEndToEndDelay.end(); l++)
+   {
+      delete l->second;
+      histEndToEndDelay.erase(l);
+   }
+   rcvdPacketsPerAssoc.clear();
+   sentPacketsPerAssoc.clear();
+   rcvdBytesPerAssoc.clear();
+}
diff --git a/src/applications/sctpapp/SCTPNatPeer.h b/src/applications/sctpapp/SCTPNatPeer.h
new file mode 100644
index 0000000..4622724
--- /dev/null
+++ b/src/applications/sctpapp/SCTPNatPeer.h
@@ -0,0 +1,135 @@
+//
+// Copyright 2004 Andras Varga
+//
+// This library is free software, you can redistribute it and/or modify
+// it under  the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation;
+// either version 2 of the License, or any later version.
+// The library 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 Lesser General Public License for more details.
+//
+
+#ifndef __SCTPNATPEER_H_
+#define __SCTPNATPEER_H_
+#include <omnetpp.h>
+#include "SCTPAssociation.h"
+#include "SCTPSocket.h"
+
+
+/**
+ * Accepts any number of incoming connections, and sends back whatever
+ * arrives on them.
+ */
+
+class INET_API SCTPNatPeer : public cSimpleModule, public SCTPSocket::CallbackInterface
+{
+	protected:
+		//SCTPAssociation* assoc;
+		int32 notifications;
+		int32 serverAssocId;
+		int32 clientAssocId;
+		//SCTPSocket *serverSocket;
+		SCTPSocket clientSocket;
+		double delay;
+		double echoFactor;
+		bool schedule;
+		bool shutdownReceived;
+		//long bytesRcvd;
+		int64 bytesSent;
+		int32 packetsSent;
+		int32 packetsRcvd;
+		int32 numSessions;
+		int32 numRequestsToSend; // requests to send in this session
+		bool ordered;
+		int32 queueSize;
+		cMessage *timeoutMsg;
+		int32 outboundStreams;
+		cMessage *timeMsg;
+		int32 bytesRcvd;
+		int32 echoedBytesSent;
+		int32 lastStream;
+		bool sendAllowed;
+		int32 chunksAbandoned;
+		int32 numPacketsToReceive;
+		bool rendezvous;
+		IPvXAddress peerAddress;
+		int32 peerPort;
+		AddressVector peerAddressList;
+		AddressVector localAddressList;
+		//cOutVector* rcvdBytes;
+		typedef std::map<int32,int64> RcvdPacketsPerAssoc;
+		RcvdPacketsPerAssoc rcvdPacketsPerAssoc;
+		typedef std::map<int32,int64> SentPacketsPerAssoc;
+		SentPacketsPerAssoc sentPacketsPerAssoc;
+		typedef std::map<int32,int64> RcvdBytesPerAssoc;
+		RcvdBytesPerAssoc rcvdBytesPerAssoc;
+		typedef std::map<int32,cOutVector*> BytesPerAssoc;
+		BytesPerAssoc bytesPerAssoc;
+		typedef std::map<int32,cDoubleHistogram*> HistEndToEndDelay;
+		HistEndToEndDelay histEndToEndDelay;
+		typedef std::map<int32,cOutVector*> EndToEndDelay;
+		EndToEndDelay endToEndDelay;
+		void sendOrSchedule(cPacket *msg);
+		void sendRequest(bool last=true);
+		int32 ssn;
+	public:
+		//Module_Class_Members(SCTPNatPeer, cSimpleModule, 0);
+		struct pathStatus {
+			bool active;
+			bool primaryPath;
+			IPAddress  pid;
+		};
+		typedef std::map<IPvXAddress,pathStatus> SCTPPathStatus;
+		SCTPPathStatus sctpPathStatus;		
+		//virtual void socketStatusArrived(int32 assocId, void *yourPtr, SCTPStatusInfo *status);
+		void initialize();
+		void handleMessage(cMessage *msg);
+		void finish();
+		void handleTimer(cMessage *msg);
+		/*void setAssociation(SCTPAssociation *_assoc) { 
+		assoc = _assoc;};*/
+		void generateAndSend(SCTPConnectInfo *connectInfo);
+		void connect(IPvXAddress connectAddress, int32 connectPort);
+		void connectx(AddressVector connectAddressList, int32 connectPort);
+
+		/** Does nothing but update statistics/status. Redefine to perform or schedule first sending. */
+		void socketEstablished(int32 connId, void *yourPtr);
+		
+		/**
+		* Does nothing but update statistics/status. Redefine to perform or schedule next sending.
+		* Beware: this funcion deletes the incoming message, which might not be what you want.
+		*/
+		void socketDataArrived(int32 connId, void *yourPtr, cPacket *msg, bool urgent);
+		
+		void socketDataNotificationArrived(int32 connId, void *yourPtr, cPacket *msg);
+		/** Since remote SCTP closed, invokes close(). Redefine if you want to do something else. */
+		void socketPeerClosed(int32 connId, void *yourPtr);
+		
+		/** Does nothing but update statistics/status. Redefine if you want to do something else, such as opening a new connection. */
+		void socketClosed(int32 connId, void *yourPtr);
+		
+		/** Does nothing but update statistics/status. Redefine if you want to try reconnecting after a delay. */
+		void socketFailure(int32 connId, void *yourPtr, int32 code);
+		
+		/** Redefine to handle incoming SCTPStatusInfo. */
+		void socketStatusArrived(int32 connId, void *yourPtr, SCTPStatusInfo *status);
+		//@}
+		void msgAbandonedArrived(int32 assocId);
+		//void setAssociation(SCTPAssociation *_assoc) {assoc = _assoc;};
+		
+		void setPrimaryPath ();
+		void sendStreamResetNotification();
+		void sendRequestArrived();
+		void sendQueueRequest();
+		void shutdownReceivedArrived(int32 connId);
+		void sendqueueFullArrived(int32 connId);
+		void addressAddedArrived(int32 assocId, IPvXAddress localAddr, IPvXAddress remoteAddr);
+		void setStatusString(const char *s);
+};
+
+#endif
+
+
+
diff --git a/src/applications/sctpapp/SCTPNatPeer.ned b/src/applications/sctpapp/SCTPNatPeer.ned
new file mode 100644
index 0000000..8bf5b4e
--- /dev/null
+++ b/src/applications/sctpapp/SCTPNatPeer.ned
@@ -0,0 +1,76 @@
+//
+// Copyright (C) 2008 Irene Ruengeler
+//
+// 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
+// of the License, 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 this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+
+
+//
+// Accepts any number of incoming TCP connections, and sends back the
+// messages that arrive on them, The lengths of the messages are
+// multiplied by echoFactor before sending them back (echoFactor=1 will
+// result in sending back the same message unmodified.) The reply can also be
+// delayed by a constant time (echoDelay parameter).
+//
+// When TCPEchoApp receives data packets from TCP (and such, when they can be
+// echoed) depends on the sendQueue/receiveQueue setting of TCPMain.
+// With TCPVirtualBytesSendQueue/RcvQueue, TCP passes up data to us
+// as soon as a segment arrives, so it can be echoed immediately.
+// With TCPMsgBasedSendQueue/RcvQueue, our local TCP reproduces the same
+// messages that the sender app passed down to its TCP -- so if the sender
+// app sent a single 100 MB message, it will be echoed only when all
+// 100 megabytes have arrived.
+//
+
+package inet.applications.sctpapp;
+
+simple SCTPNatPeer like SCTPApp
+{
+   parameters:
+      string address = default(""); // may be left empty ("")
+      int port = default(0); // port number to listen on
+      string connectAddress;  // server address (may be symbolic)
+      int connectPort; // port number to connect to
+      double startTime @unit(s) = default(1s); // time first session begins
+      int numRequestsPerSession = default(1);  // number of requests sent per session
+      int numPacketsToReceive = default(0);
+      int requestLength = default(1452); // length of a request (bytes)
+      double thinkTime @unit(s) = default(0s); // time gap between requests
+      double waitToClose @unit(s) = default(0s); //time to wait between last message sent and abort
+      //double reconnectInterval @unit(s) = default(0);  // if connection breaks, waits this much before trying to reconnect
+      int outboundStreams = default(1);
+      int echoFactor = default(0);
+      bool ordered = default(true);
+      int queueSize = default(0);
+      int prMethod = default(0);  //0=NONE, 1=PR_TTL, 2=PR_RTX, 3=PR_PRIO, 4=PR_STRRST
+      bool streamReset = default(false);
+      double streamRequestTime @unit(s) = default(0s);
+      int streamResetType = default(5);  //NO_RESET=5, RESET_OUTGOING=6, RESET_INCOMING=7, RESET_BOTH=8, SSN_TSN=9
+      bool streamAnswer = default(false);
+      double prValue = default(0); //for PR-SCTP
+      //double stopTime @unit(s) = default(0s);
+      double echoDelay @unit(s) = default(0s);
+      double delayFirstRead @unit(s) = default(0s);
+      int numPacketsToSendPerClient = default(0);  // number of requests sent per session
+      int numPacketsToReceivePerClient = default(1);
+      int ownName;
+      int peerName;
+      bool rendezvous = default(false);
+      bool multi= default(false);
+      string streamPriorities = default("");
+   gates:
+      input sctpIn;
+      output sctpOut;
+}
diff --git a/src/applications/sctpapp/SCTPNatServer.cc b/src/applications/sctpapp/SCTPNatServer.cc
new file mode 100644
index 0000000..75e159d
--- /dev/null
+++ b/src/applications/sctpapp/SCTPNatServer.cc
@@ -0,0 +1,494 @@
+//
+// Copyright 2005 Irene Ruengeler
+//
+// This library is free software, you can redistribute it and/or modify
+// it under  the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation;
+// either version 2 of the License, or any later version.
+// The library 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 Lesser General Public License for more details.
+//
+
+#include "SCTPNatServer.h"
+#include "SCTPSocket.h"
+#include "SCTPCommand_m.h"
+#include "SCTPMessage_m.h"
+#include "IPvXAddress.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include "SCTPAssociation.h"
+
+#define MSGKIND_CONNECT  0
+#define MSGKIND_SEND     1
+#define MSGKIND_    2
+
+Define_Module(SCTPNatServer);
+
+NatVector SCTPNatServer::natVector;
+
+void SCTPNatServer::initialize()
+{
+   char * token;
+   AddressVector addresses;
+
+   numSessions = packetsSent = packetsRcvd = bytesSent = notifications = 0;
+   WATCH(numSessions);
+   WATCH(packetsSent);
+   WATCH(packetsRcvd);
+   WATCH(bytesSent);
+   //WATCH(rcvdBytesPerAssoc);
+   WATCH(numRequestsToSend);
+   // parameters
+   const char* address = par("address");
+   token = strtok((char*)address,",");
+   while (token != NULL)
+   {
+      //sctpEV3<<"address="<<token<<"\n";
+      addresses.push_back(IPvXAddress(token));
+      token = strtok(NULL, ",");
+   }
+   int32 port = par("port");
+   echoFactor = par("echoFactor");
+   delay = par("echoDelay");
+   outboundStreams = par("outboundStreams");
+   ordered = (bool)par("ordered");
+   queueSize = par("queueSize");
+   lastStream = 0;
+   timeoutMsg = new cMessage("SrvAppTimer");
+   SCTPSocket *socket = new SCTPSocket();
+   socket->setOutputGate(gate("sctpOut"));
+   socket->setOutboundStreams(outboundStreams);
+   if (strcmp(address,"")==0)
+      socket->bind(port);
+   else
+   {
+      socket->bindx(addresses, port);
+   }
+   socket->listen(true,(bool)par("streamReset"), par("numPacketsToSendPerClient"));
+   sctpEV3<<"SCTPNatServer::initialized listen port="<<port<<"\n";
+
+   schedule = false;
+   shutdownReceived = false;
+   uint32 streamNum = 0;
+   cStringTokenizer tokenizer(par("streamPriorities").stringValue());
+   while (tokenizer.hasMoreTokens())
+   {
+      const char *token = tokenizer.nextToken();
+      socket->setStreamPriority(streamNum, (uint32) atoi(token));
+
+      streamNum++;
+   }
+}
+
+void SCTPNatServer::sendOrSchedule(cPacket *msg)
+{
+   if (delay==0)
+   {
+      send(msg, "sctpOut");
+   }
+   else
+   {
+      scheduleAt(simulation.getSimTime()+delay, msg);
+   }
+}
+
+
+void SCTPNatServer::sendInfo(NatInfo* info)
+{
+   NatMessage* msg = new NatMessage("Rendezvous");
+   msg->setKind(SCTP_C_NAT_INFO);
+   msg->setMulti(info->multi);
+   msg->setPeer1(info->peer1);
+   msg->setPeer1AddressesArraySize(2);
+   msg->setPeer1Addresses(0,info->peer1Address1);
+   msg->setPeer1Addresses(1,info->peer1Address2);
+   msg->setPortPeer1(info->peer1Port);
+   msg->setPeer2(info->peer2);
+   msg->setPeer2AddressesArraySize(2);
+   msg->setPeer2Addresses(0,info->peer2Address1);
+   msg->setPeer2Addresses(1,info->peer2Address2);
+   msg->setPortPeer2(info->peer2Port);
+   sctpEV3<<"Info for peer1: peer1-1="<<msg->getPeer1Addresses(0)<<" peer1-2="<<msg->getPeer1Addresses(1)<<" peer2-1="<<msg->getPeer2Addresses(0)<<" peer2-2="<<msg->getPeer2Addresses(1)<<"\n";
+   cPacket* cmsg = new cPacket(msg->getName());
+   SCTPSimpleMessage* smsg=new SCTPSimpleMessage("nat_data");
+   smsg->setEncaps(true);
+   smsg->encapsulate(msg);
+   smsg->setCreationTime(simulation.getSimTime());
+   smsg->setBitLength(16*8);
+   cmsg->encapsulate(PK(smsg));
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(info->peer1Assoc);
+   cmd->setGate(info->peer1Gate);
+   cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
+   cmd->setSid(0);
+   cmd->setPrValue(0);
+   cmd->setPrMethod((int32)par("prMethod"));
+   cmd->setLast(true);
+   cmsg->setKind(SCTP_C_SEND);
+   cmsg->setControlInfo(cmd);
+   sendOrSchedule(cmsg);
+   sctpEV3<<"info sent to peer1\n";
+   cPacket* abortMsg = new cPacket("abortPeer1",SCTP_C_SHUTDOWN);
+   abortMsg->setControlInfo(cmd->dup());
+   sendOrSchedule(abortMsg);
+   sctpEV3<<"abortMsg sent to peer1\n";
+
+   msg = new NatMessage("Rendezvous");
+   msg->setKind(SCTP_C_NAT_INFO);
+   msg->setMulti(info->multi);
+   msg->setPeer1(info->peer2);
+   msg->setPeer1AddressesArraySize(2);
+   msg->setPeer1Addresses(0,info->peer2Address1);
+   msg->setPeer1Addresses(1,info->peer2Address2);
+   msg->setPortPeer1(info->peer2Port);
+   msg->setPeer2(info->peer1);
+   msg->setPeer2AddressesArraySize(2);
+   msg->setPeer2Addresses(0,info->peer1Address1);
+   msg->setPeer2Addresses(1,info->peer1Address2);
+   msg->setPortPeer2(info->peer1Port);
+   sctpEV3<<"Info for peer2: peer1-1="<<msg->getPeer1Addresses(0)<<" peer1-2="<<msg->getPeer1Addresses(1)<<" peer2-1="<<msg->getPeer2Addresses(0)<<" peer2-2="<<msg->getPeer2Addresses(1)<<"\n";
+   cmsg = new cPacket(msg->getName());
+   smsg=new SCTPSimpleMessage("nat_data");
+   smsg->setEncaps(true);
+   smsg->encapsulate(msg);
+   smsg->setCreationTime(simulation.getSimTime());
+   smsg->setBitLength(16*8);
+   cmsg->encapsulate(PK(smsg));
+   cmd = new SCTPSendCommand();
+   cmd->setAssocId(info->peer2Assoc);
+   cmd->setGate(info->peer2Gate);
+   cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
+   cmd->setSid(0);
+   cmd->setPrValue(0);
+   cmd->setPrMethod((int32)par("prMethod"));
+   cmd->setLast(true);
+   cmsg->setKind(SCTP_C_SEND);
+   cmsg->setControlInfo(cmd);
+   sctpEV3<<"info sent to peer2\n";
+   sendOrSchedule(cmsg);
+   abortMsg = new cPacket("abortPeer2",SCTP_C_SHUTDOWN);
+   abortMsg->setControlInfo(cmd->dup());
+   sendOrSchedule(abortMsg);
+   sctpEV3<<"abortMsg sent to peer1\n";
+}
+
+void SCTPNatServer::generateAndSend(SCTPConnectInfo *connectInfo)
+{
+uint32 numBytes;
+   //sctpEV3 << "SCTPNatServer:generateAndSend \n";
+
+   cPacket* cmsg = new cPacket("CMSG");
+   SCTPSimpleMessage* msg=new SCTPSimpleMessage("Server");
+   numBytes=(int64)(long)par("requestLength");
+   msg->setDataArraySize(numBytes);
+   for (uint32 i=0; i<numBytes; i++)
+   {
+      msg->setData(i, 's');
+   }
+   msg->setDataLen(numBytes);
+   msg->setEncaps(false);
+   msg->setBitLength(numBytes * 8);
+   cmsg->encapsulate(PK(msg));
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(assocId);
+   if (ordered)
+      cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
+   else
+      cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
+   lastStream=(lastStream+1)%outboundStreams;
+   cmd->setSid(lastStream);
+   cmd->setPrValue((double)par("prValue"));
+   cmd->setPrMethod((int32)par("prMethod"));
+   cmd->setLast(true);
+   cmsg->setKind(SCTP_C_SEND);
+   cmsg->setControlInfo(cmd);
+   packetsSent++;
+   bytesSent+=msg->getBitLength()/8;
+   sendOrSchedule(cmsg);
+}
+
+void SCTPNatServer::handleMessage(cMessage *msg)
+{
+   int32 id;
+
+
+   ////sctpEV3<<"SCTPNatServer::handleMessage kind="<<SCTPAssociation::indicationName(msg->getKind())<<" ("<<msg->getKind()<<")\n";
+
+   if (msg->isSelfMessage())
+   {
+
+      handleTimer(msg);
+   }
+   else
+   switch (msg->getKind())
+   {
+      case SCTP_I_PEER_CLOSED:
+      case SCTP_I_ABORT:
+      {
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->getControlInfo()->dup());
+         cPacket* cmsg = new cPacket("Notification");
+         SCTPSendCommand *cmd = new SCTPSendCommand();
+         id = ind->getAssocId();
+         cmd->setAssocId(id);
+         cmd->setSid(ind->getSid());
+         cmd->setNumMsgs(ind->getNumMsgs());
+         cmsg->setControlInfo(cmd);
+         delete ind;
+         delete msg;
+         cmsg->setKind(SCTP_C_ABORT);
+         sendOrSchedule(cmsg);
+         break;
+      }
+      case SCTP_I_ESTABLISHED:
+      {
+         SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->removeControlInfo());
+         numSessions++;
+         assocId = connectInfo->getAssocId();
+         id = assocId;
+         outboundStreams = connectInfo->getOutboundStreams();
+
+         delete connectInfo;
+         delete msg;
+
+         break;
+      }
+      case SCTP_I_DATA_NOTIFICATION:
+      {
+         notifications++;
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         cPacket* cmsg = new cPacket("Notification");
+         SCTPSendCommand *cmd = new SCTPSendCommand();
+         id = ind->getAssocId();
+         cmd->setAssocId(id);
+         cmd->setSid(ind->getSid());
+         cmd->setNumMsgs(ind->getNumMsgs());
+         cmsg->setKind(SCTP_C_RECEIVE);
+         cmsg->setControlInfo(cmd);
+         delete ind;
+         delete msg;
+         if (!cmsg->isScheduled() && schedule==false)
+         {
+            scheduleAt(simulation.getSimTime()+(simtime_t)par("delayFirstRead"), cmsg);
+         }
+         else if (schedule==true)
+            sendOrSchedule(cmsg);
+         break;
+      }
+      case SCTP_I_DATA:
+      {
+         //bytesRcvd += msg->getBitLength()/8;
+         sctpEV3<<"\nData arrived at server\n";
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         id = ind->getAssocId();
+
+         SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg);
+         NatMessage* nat = check_and_cast <NatMessage*>(smsg->decapsulate());
+         bool found=false;
+         if (natVector.size()>0)
+         {
+            sctpEV3<<"natVector.size>0\n";
+            for (NatVector::iterator it=natVector.begin(); it!=natVector.end(); it++)
+            {
+               if ((*it)->peer1==nat->getPeer1())
+               {
+                  sctpEV3<<"found entry for Peer1 = "<<nat->getPeer1()<<"  peer1Address1="<<(*it)->peer1Address1<<" peer1Address2="<<(*it)->peer1Address2<<" peer2Address1="<<(*it)->peer2Address1<<  " peer2Address2="<<(*it)->peer2Address2<<"\n";
+                  if (nat->getMulti() && (*it)->peer1Address2==IPvXAddress("0.0.0.0"))
+                  {
+                     (*it)->peer1Address2=ind->getRemoteAddr();
+                  }
+                  if ((*it)->peer2Address1!=IPvXAddress("0.0.0.0"))
+                  {
+
+                     if (!(*it)->multi || ((*it)->multi && (*it)->peer2Address2!=IPvXAddress("0.0.0.0") && (*it)->peer1Address2!=IPvXAddress("0.0.0.0")))
+                     {
+                        sctpEV3<<"entry now: Peer1="<<(*it)->peer1<<" Peer2="<<(*it)->peer2<<" peer1Address1="<<(*it)->peer1Address1<<" peer1Address2="<<(*it)->peer1Address2<<" peer2Address1="<<(*it)->peer2Address1<<" peer2Address2="<<(*it)->peer2Address2<<" peer2Port="<<(*it)->peer2Port<<"\n";
+                        sendInfo((*it));
+                     }
+                  }
+                  found = true;
+                  break;
+               }
+               if ((*it)->peer2==nat->getPeer1())
+               {
+                  sctpEV3<<"opposite way: found entry for Peer1 = "<<nat->getPeer1()<<"  peer1Address1="<<(*it)->peer1Address1<<" peer1Address2="<<(*it)->peer1Address2<<" peer2Address1="<<(*it)->peer2Address1<<  " peer2Address2="<<(*it)->peer2Address2<<"\n";
+                  if ((*it)->peer2Address1==IPvXAddress("0.0.0.0"))
+                  {
+                     (*it)->peer2Address1=ind->getRemoteAddr();
+                     (*it)->peer2Assoc = assocId;
+                     (*it)->peer2Port = nat->getPortPeer1();
+                     (*it)->peer2Gate = ind->getGate();
+                     sctpEV3<<"set peer2Address1="<<ind->getRemoteAddr()<<" peer2Assoc="<<assocId<<" peer2Port="<<nat->getPortPeer1()<<"\n";
+                  }
+                  else if ((*it)->multi)
+                     (*it)->peer2Address2=ind->getRemoteAddr();
+
+                  if (!(*it)->multi || ((*it)->multi && (*it)->peer2Address2!=IPvXAddress("0.0.0.0") && (*it)->peer1Address2!=IPvXAddress("0.0.0.0")))
+                  {
+                     sctpEV3<<"entry now: Peer1="<<(*it)->peer1<<" Peer2="<<(*it)->peer2<<" peer1Address1="<<(*it)->peer1Address1<<" peer1Address2="<<(*it)->peer1Address2<<" peer2Address1="<<(*it)->peer2Address1<<" peer2Address2="<<(*it)->peer2Address2<<" peer1Port="<<(*it)->peer1Port<<"peer2Port="<<(*it)->peer2Port<<"\n";
+                     sendInfo((*it));
+                  }
+                  found = true;
+                  break;
+               }
+            }
+         }
+         if (natVector.size()==0 || !found)
+         {
+            sctpEV3<<"make new Info for ";
+            NatInfo* info=new NatInfo();
+            info->peer1 = nat->getPeer1();
+            sctpEV3<<info->peer1<<" and assoc "<<assocId<<"\n";;
+            info->multi = nat->getMulti();
+            info->peer1Address1 = ind->getRemoteAddr();
+            if (info->multi)
+            {
+               info->peer1Address2 = IPvXAddress("0.0.0.0");
+               info->peer2Address2 = IPvXAddress("0.0.0.0");
+            }
+            info->peer1Port = nat->getPortPeer1();
+            info->peer1Assoc = assocId;
+            info->peer1Gate = ind->getGate();
+            info->peer2 = nat->getPeer2();
+            info->peer2Address1 = IPvXAddress("0.0.0.0");
+            info->peer2Port = 0;
+            info->peer2Assoc = 0;
+            info->peer2Gate = -1;
+            natVector.push_back(info);
+            sctpEV3<<"Info: peer1="<<info->peer1<<" peer1Address1="<<info->peer1Address1<<" peer1Address2="<<info->peer1Address2<<" peer1Assoc="<<info->peer1Assoc<<"\n peer2="<<info->peer1<<" peer2Address1="<<info->peer2Address1<<" peer2Address2="<<info->peer2Address2<<" peer2Assoc="<<info->peer2Assoc<<"\n";
+         }
+sctpEV3<<"\n";
+         delete msg;
+
+         delete ind;
+         delete nat;
+         break;
+      }
+      case SCTP_I_SHUTDOWN_RECEIVED:
+      {
+         SCTPCommand *command = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         id = command->getAssocId();
+         sctpEV3<<"server: SCTP_I_SHUTDOWN_RECEIVED for assoc "<<id<<"\n";
+         cPacket* cmsg = new cPacket("Request");
+         SCTPInfo* qinfo = new SCTPInfo("Info");
+         cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+         qinfo->setAssocId(id);
+         cmsg->setControlInfo(qinfo);
+         sendOrSchedule(cmsg);
+
+         delete command;
+         shutdownReceived = true;
+         delete msg;
+      }
+      case SCTP_I_SEND_STREAMS_RESETTED:
+      case SCTP_I_RCV_STREAMS_RESETTED:
+      {
+         ev<< "Streams have been resetted\n";
+         delete msg;
+         break;
+      }
+
+      case SCTP_I_CLOSED: delete msg;
+      break;
+      case SCTP_I_ADDRESS_ADDED:
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         sctpEV3<<" address added: LOCAL="<<ind->getLocalAddr()<<", remote="<<ind->getRemoteAddr()<<" assoc="<<assocId<<"\n";
+         if (natVector.size()>0)
+         {
+            sctpEV3<<"natVector.size>0\n";
+            for (NatVector::iterator it=natVector.begin(); it!=natVector.end(); it++)
+            {
+               if ((*it)->peer1Assoc==assocId)
+               {
+                  sctpEV3<<"found entry for assoc1 = "<<assocId<<"  peer1Address1="<<(*it)->peer1Address1<<" peer1Address2="<<(*it)->peer1Address2<<" peer2Address1="<<(*it)->peer2Address1<<  " peer2Address2="<<(*it)->peer2Address2<<"\n";
+                  if ((*it)->multi && (*it)->peer1Address2==IPvXAddress("0.0.0.0"))
+                  {
+                     (*it)->peer1Address2=ind->getRemoteAddr();
+                     sctpEV3<<"added peer1Address2="<<ind->getRemoteAddr()<<"\n";
+                  }
+                  if ((*it)->peer2Address1!=IPvXAddress("0.0.0.0"))
+                  {
+
+                     if (!(*it)->multi || ((*it)->multi && (*it)->peer2Address2!=IPvXAddress("0.0.0.0") && (*it)->peer1Address2!=IPvXAddress("0.0.0.0")))
+                     {
+                        sctpEV3<<"entry now: Peer1="<<(*it)->peer1<<" Peer2="<<(*it)->peer2<<" peer1Address1="<<(*it)->peer1Address1<<" peer1Address2="<<(*it)->peer1Address2<<" peer2Address1="<<(*it)->peer2Address1<<" peer2Address2="<<(*it)->peer2Address2<<" peer2Port="<<(*it)->peer2Port<<"\n";
+                        sendInfo((*it));
+                     }
+                  }
+                  break;
+               }
+               else if ((*it)->peer2Assoc==assocId)
+               {
+                  sctpEV3<<"opposite way: found entry for assoc2 = "<<assocId<<"  peer1Address1="<<(*it)->peer1Address1<<" peer1Address2="<<(*it)->peer1Address2<<" peer2Address1="<<(*it)->peer2Address1<<  " peer2Address2="<<(*it)->peer2Address2<<"\n";
+                  if ((*it)->multi)
+                     (*it)->peer2Address2=ind->getRemoteAddr();
+
+                  if (!(*it)->multi || ((*it)->multi && (*it)->peer2Address2!=IPvXAddress("0.0.0.0") && (*it)->peer1Address2!=IPvXAddress("0.0.0.0")))
+                  {
+                     sctpEV3<<"entry now: Peer1="<<(*it)->peer1<<" Peer2="<<(*it)->peer2<<" peer1Address1="<<(*it)->peer1Address1<<" peer1Address2="<<(*it)->peer1Address2<<" peer2Address1="<<(*it)->peer2Address1<<" peer2Address2="<<(*it)->peer2Address2<<" peer1Port="<<(*it)->peer1Port<<"peer2Port="<<(*it)->peer2Port<<"\n";
+                     sendInfo((*it));
+                  }
+                  break;
+               }
+               else if ((*it)->peer2Assoc==0 && ((*it)->multi))
+               {
+                  (*it)->peer2Address2=ind->getRemoteAddr();
+                  (*it)->peer2Assoc = assocId;
+               }
+            }
+         }
+         delete ind;
+         delete msg;
+         break;
+   }
+
+
+}
+
+void SCTPNatServer::handleTimer(cMessage *msg)
+{
+   cPacket* cmsg;
+   int32 id;
+
+   SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->getControlInfo());
+   switch (msg->getKind())
+   {
+   case SCTP_C_SEND:
+      if (numRequestsToSend>0)
+      {
+         generateAndSend(connectInfo);
+         if ((simtime_t)par("thinkTime") > 0)
+            scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
+         numRequestsToSend--;
+      }
+      break;
+   case SCTP_I_ABORT:
+   {
+      cmsg = new cPacket("CLOSE", SCTP_C_CLOSE);
+      SCTPCommand* cmd = new SCTPCommand();
+      id = atoi(msg->getName());
+           cmd->setAssocId(id);
+      cmsg->setControlInfo(cmd);
+      sendOrSchedule(cmsg);
+      break;
+   }
+   case SCTP_C_RECEIVE:
+      schedule = true;
+      sendOrSchedule(PK(msg));
+      break;
+   default:
+      break;
+   }
+   delete connectInfo;
+}
+
+void SCTPNatServer::finish()
+{
+   delete timeoutMsg;
+      ev << getFullPath() << ": opened " << numSessions << " sessions\n";
+   ev << getFullPath() << ": sent " << bytesSent << " bytes in " << packetsSent << " packets\n";
+
+   ev << getFullPath() << "Over all " << packetsRcvd << " packets received\n ";
+   ev << getFullPath() << "Over all " << notifications << " notifications received\n ";
+}
diff --git a/src/applications/sctpapp/SCTPNatServer.h b/src/applications/sctpapp/SCTPNatServer.h
new file mode 100644
index 0000000..54e0f1f
--- /dev/null
+++ b/src/applications/sctpapp/SCTPNatServer.h
@@ -0,0 +1,94 @@
+//
+// Copyright 2007 Irene Ruengeler
+//
+// This library is free software, you can redistribute it and/or modify
+// it under  the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation;
+// either version 2 of the License, or any later version.
+// The library 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 Lesser General Public License for more details.
+//
+
+#ifndef __SCTPSERVER_H_
+#define __SCTPSERVER_H_
+
+#include <omnetpp.h>
+#include "SCTPAssociation.h"
+#include "SCTPSocket.h"
+
+
+/**
+ * Accepts any number of incoming connections, and sends back whatever
+ * arrives on them.
+ */
+
+typedef struct natInfo {
+			bool multi;
+			uint32 peer1;
+			IPvXAddress peer1Address1;
+			IPvXAddress peer1Address2;
+			uint32 peer1Assoc;
+			uint32 peer1Port;
+			int32 peer1Gate;
+			uint32 peer2;
+			IPvXAddress peer2Address1;
+			IPvXAddress peer2Address2;
+			uint32 peer2Assoc;
+			uint32 peer2Port;
+			int32 peer2Gate;
+		} NatInfo;
+typedef std::vector<NatInfo*> NatVector;
+
+class INET_API SCTPNatServer : public cSimpleModule
+{
+	protected:
+		//SCTPAssociation* assoc;
+		int32 notifications;
+		uint32 assocId;
+		SCTPSocket *socket;
+		double delay;
+		double echoFactor;
+		bool schedule;
+		bool shutdownReceived;
+		//long bytesRcvd;
+		int64 bytesSent;
+		int32 packetsSent;
+		int32 packetsRcvd;
+		int32 numSessions;
+		int32 numRequestsToSend; // requests to send in this session
+		bool ordered;
+		int32 queueSize;
+		cMessage *timeoutMsg;
+		int32 outboundStreams;
+		int32 lastStream;
+		//cOutVector* rcvdBytes;
+		
+		static NatVector natVector;
+
+		void sendOrSchedule(cPacket *msg);
+		int32 ssn;
+	public:
+		//SCTPNatServer();
+		//Module_Class_Members(SCTPNatServer, cSimpleModule, 0);
+		struct pathStatus {
+			bool active;
+			bool primaryPath;
+			IPAddress  pid;
+		};
+		
+		//virtual void socketStatusArrived(int32 assocId, void *yourPtr, SCTPStatusInfo *status);
+		void initialize();
+		void handleMessage(cMessage *msg);
+		void finish();
+		void handleTimer(cMessage *msg);
+		/*void setAssociation(SCTPAssociation *_assoc) { 
+		assoc = _assoc;};*/
+		void generateAndSend(SCTPConnectInfo *connectInfo);
+		void sendInfo(NatInfo* info);
+};
+
+#endif
+
+
diff --git a/src/applications/sctpapp/SCTPNatServer.ned b/src/applications/sctpapp/SCTPNatServer.ned
new file mode 100644
index 0000000..37aaa4e
--- /dev/null
+++ b/src/applications/sctpapp/SCTPNatServer.ned
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2008 Irene Ruengeler
+//
+// 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
+// of the License, 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 this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+
+package inet.applications.sctpapp;
+
+simple SCTPNatServer like SCTPApp
+{
+   parameters:
+      string address = default(""); // may be left empty ("")
+      int port = default(0); // port number to listen on
+      int numPacketsToSendPerClient = default(0);  // number of requests sent per session
+      int numPacketsToReceivePerClient = default(1);
+      double echoDelay @unit(s) = default(0s);
+      int echoFactor = default(0);
+      double delayFirstRead @unit(s) = default(0s);
+      int requestLength = default(1452); // length of a request (bytes)
+      double thinkTime @unit(s) = default(0s); // time gap between requests
+      double waitToClose @unit(s) = default(0s); //time to wait between last message sent and abort
+      int outboundStreams = default(1);
+      bool ordered = default(true);
+      int prMethod = default(0);  //0=NONE, 1=PR_TTL, 2=PR_RTX, 3=PR_PRIO, 4=PR_STRRST
+      bool streamReset = default(false);
+      double prValue = default(0); //for PR-SCTP
+      int queueSize = default(0);
+      string streamPriorities = default("");
+   gates:
+      input sctpIn;
+      output sctpOut;
+}
diff --git a/src/applications/sctpapp/SCTPPeer.cc b/src/applications/sctpapp/SCTPPeer.cc
index 763fedf..4188ce1 100644
--- a/src/applications/sctpapp/SCTPPeer.cc
+++ b/src/applications/sctpapp/SCTPPeer.cc
@@ -8,7 +8,7 @@
 //
 // 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
+// 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
@@ -26,581 +26,611 @@
 #include "IPAddressResolver.h"
 
 #define MSGKIND_CONNECT  0
-#define MSGKIND_SEND         1
+#define MSGKIND_SEND     1
 #define MSGKIND_ABORT    2
 #define MSGKIND_PRIMARY  3
+#define MSGKIND_RESET    4
 #define MSGKIND_STOP     5
 
 Define_Module(SCTPPeer);
 
 void SCTPPeer::initialize()
 {
-    char * token;
-    AddressVector addresses;
-
-    numSessions = packetsSent = packetsRcvd = bytesSent = notifications = 0;
-    WATCH(numSessions);
-    WATCH(packetsSent);
-    WATCH(packetsRcvd);
-    WATCH(bytesSent);
-    WATCH(numRequestsToSend);
-    // parameters
-    const char* address = par("address");
-
-    token = strtok((char*)address,",");
-    while (token != NULL)
-    {
-        addresses.push_back(IPvXAddress(token));
-        token = strtok(NULL, ",");
-    }
-    int32 port = par("port");
-    echoFactor = par("echoFactor");
-    delay = par("echoDelay");
-    outboundStreams = par("outboundStreams");
-    ordered = (bool)par("ordered");
-    queueSize = par("queueSize");
-    lastStream = 0;
-    timeoutMsg = new cMessage("SrvAppTimer");
-    SCTPSocket* socket = new SCTPSocket();
-    socket->setOutputGate(gate("sctpOut"));
-    socket->setOutboundStreams(outboundStreams);
-    if (strcmp(address,"")==0)
-    {
-        socket->bind(port);
-        clientSocket.bind(port);
-    }
-    else
-    {
-        socket->bindx(addresses, port);
-        clientSocket.bindx(addresses, port);
-    }
-    socket->listen(true, par("numPacketsToSendPerClient"));
-    sctpEV3<<"SCTPPeer::initialized listen port="<<port<<"\n";
-    clientSocket.setCallbackObject(this);
-    clientSocket.setOutputGate(gate("sctpOut"));
-
-    if ((simtime_t)par("startTime")>0)
-    {
-        connectTimer = new cMessage("ConnectTimer");
-        connectTimer->setKind(MSGKIND_CONNECT);
-        scheduleAt((simtime_t)par("startTime"), connectTimer);
-    }
-    schedule = false;
-    shutdownReceived = false;
-    sendAllowed = true;
+   char * token;
+   AddressVector addresses;
+
+   numSessions = packetsSent = packetsRcvd = bytesSent = notifications = 0;
+   WATCH(numSessions);
+   WATCH(packetsSent);
+   WATCH(packetsRcvd);
+   WATCH(bytesSent);
+   WATCH(numRequestsToSend);
+   // parameters
+   const char* address = par("address");
+
+   token = strtok((char*)address,",");
+   while (token != NULL)
+   {
+      addresses.push_back(IPvXAddress(token));
+      token = strtok(NULL, ",");
+   }
+   int32 port = par("port");
+   echoFactor = par("echoFactor");
+   delay = par("echoDelay");
+   outboundStreams = par("outboundStreams");
+   ordered = (bool)par("ordered");
+   queueSize = par("queueSize");
+   lastStream = 0;
+   timeoutMsg = new cMessage("SrvAppTimer");
+   SCTPSocket* socket = new SCTPSocket();
+   socket->setOutputGate(gate("sctpOut"));
+   socket->setOutboundStreams(outboundStreams);
+   if (strcmp(address,"")==0)
+   {
+      socket->bind(port);
+      clientSocket.bind(port);
+   }
+   else
+   {
+      socket->bindx(addresses, port);
+      clientSocket.bindx(addresses, port);
+   }
+   socket->listen(true,(bool)par("streamReset"), par("numPacketsToSendPerClient"));
+   sctpEV3<<"SCTPPeer::initialized listen port="<<port<<"\n";
+   clientSocket.setCallbackObject(this);
+   clientSocket.setOutputGate(gate("sctpOut"));
+
+   if ((simtime_t)par("startTime")>0)
+   {
+      connectTimer = new cMessage("ConnectTimer");
+      connectTimer->setKind(MSGKIND_CONNECT);
+      scheduleAt((simtime_t)par("startTime"), connectTimer);
+   }
+   schedule = false;
+   shutdownReceived = false;
+   sendAllowed = true;
 }
 
 void SCTPPeer::sendOrSchedule(cPacket *msg)
 {
-    if (delay==0)
-    {
-        send(msg, "sctpOut");
-    }
-    else
-    {
-        scheduleAt(simulation.getSimTime()+delay, msg);
-    }
+   if (delay==0)
+   {
+      send(msg, "sctpOut");
+   }
+   else
+   {
+      scheduleAt(simulation.getSimTime()+delay, msg);
+   }
 }
 
 void SCTPPeer::generateAndSend(SCTPConnectInfo *connectInfo)
 {
 uint32 numBytes;
-    cPacket* cmsg = new cPacket("CMSG");
-    SCTPSimpleMessage* msg=new SCTPSimpleMessage("Server");
-    numBytes=(long)par("requestLength");
-    msg->setDataArraySize(numBytes);
-    for (uint32 i=0; i<numBytes; i++)
-    {
-        msg->setData(i, 's');
-    }
-    msg->setDataLen(numBytes);
-    msg->setByteLength(numBytes);
-    cmsg->encapsulate(msg);
-    SCTPSendCommand *cmd = new SCTPSendCommand();
-    cmd->setAssocId(serverAssocId);
-    if (ordered)
-        cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
-    else
-        cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
-    lastStream=(lastStream+1)%outboundStreams;
-    cmd->setSid(lastStream);
-    cmd->setLast(true);
-    cmsg->setKind(SCTP_C_SEND);
-    cmsg->setControlInfo(cmd);
-    packetsSent++;
-    bytesSent+=msg->getBitLength()/8;
-    sendOrSchedule(cmsg);
+   cPacket* cmsg = new cPacket("CMSG");
+   SCTPSimpleMessage* msg=new SCTPSimpleMessage("Server");
+   numBytes=(long)par("requestLength");
+   msg->setDataArraySize(numBytes);
+   for (uint32 i=0; i<numBytes; i++)
+   {
+      msg->setData(i, 's');
+   }
+   msg->setDataLen(numBytes);
+   msg->setByteLength(numBytes);
+   msg->setEncaps(false);
+   cmsg->encapsulate(msg);
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(serverAssocId);
+   if (ordered)
+      cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
+   else
+      cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
+   lastStream=(lastStream+1)%outboundStreams;
+   cmd->setSid(lastStream);
+   cmd->setPrValue(par("prValue"));
+   cmd->setPrMethod((int32)par("prMethod"));
+   cmd->setLast(true);
+   cmsg->setKind(SCTP_C_SEND);
+   cmsg->setControlInfo(cmd);
+   packetsSent++;
+   bytesSent+=msg->getBitLength()/8;
+   sendOrSchedule(cmsg);
 }
 
 void SCTPPeer::connect()
 {
-    const char *connectAddress = par("connectAddress");
-    int32 connectPort = par("connectPort");
-    uint32 outStreams = par("outboundStreams");
-    clientSocket.setOutboundStreams(outStreams);
-
-    sctpEV3 << "issuing OPEN command\n";
-    sctpEV3<<"Assoc "<<clientSocket.getConnectionId()<<"::connect to address "<<connectAddress<<", port "<<connectPort<<"\n";
-    numSessions++;
-    clientSocket.connect(IPAddressResolver().resolve(connectAddress, 1), connectPort, (uint32)par("numRequestsPerSession"));
-
+   const char *connectAddress = par("connectAddress");
+   int32 connectPort = par("connectPort");
+   uint32 outStreams = par("outboundStreams");
+   clientSocket.setOutboundStreams(outStreams);
+
+   sctpEV3 << "issuing OPEN command\n";
+   sctpEV3<<"Assoc "<<clientSocket.getConnectionId()<<"::connect to address "<<connectAddress<<", port "<<connectPort<<"\n";
+   numSessions++;
+   bool streamReset = par("streamReset");
+   clientSocket.connect(IPAddressResolver().resolve(connectAddress, 1), connectPort, streamReset, (int32)par("prMethod"), (uint32)par("numRequestsPerSession"));
+   if (!streamReset)
+      streamReset = false;
+   else if (streamReset == true)
+   {
+      cMessage* cmsg = new cMessage("StreamReset");
+      cmsg->setKind(MSGKIND_RESET);
+      sctpEV3<<"StreamReset Timer scheduled at "<<simulation.getSimTime()<<"\n";
+      scheduleAt(simulation.getSimTime()+(double)par("streamRequestTime"), cmsg);
+   }
+   uint32 streamNum = 0;
+   cStringTokenizer tokenizer(par("streamPriorities").stringValue());
+   while (tokenizer.hasMoreTokens())
+   {
+      const char *token = tokenizer.nextToken();
+      clientSocket.setStreamPriority(streamNum, (uint32) atoi(token));
+
+      streamNum++;
+   }
 }
 
 void SCTPPeer::handleMessage(cMessage *msg)
 {
-    int32 id;
-
-    if (msg->isSelfMessage())
-    {
-
-        handleTimer(msg);
-    }
-    switch (msg->getKind())
-    {
-        case SCTP_I_PEER_CLOSED:
-        case SCTP_I_ABORT:
-        {
-            SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->getControlInfo()->dup());
-            cPacket* cmsg = new cPacket("Notification");
-            SCTPSendCommand *cmd = new SCTPSendCommand();
-            id = ind->getAssocId();
-            cmd->setAssocId(id);
-            cmd->setSid(ind->getSid());
-            cmd->setNumMsgs(ind->getNumMsgs());
-            cmsg->setControlInfo(cmd);
-            delete ind;
+   int32 id;
+
+   if (msg->isSelfMessage())
+   {
+
+      handleTimer(msg);
+   }
+   switch (msg->getKind())
+   {
+      case SCTP_I_PEER_CLOSED:
+      case SCTP_I_ABORT:
+      {
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->getControlInfo()->dup());
+         cPacket* cmsg = new cPacket("Notification");
+         SCTPSendCommand *cmd = new SCTPSendCommand();
+         id = ind->getAssocId();
+         cmd->setAssocId(id);
+         cmd->setSid(ind->getSid());
+         cmd->setNumMsgs(ind->getNumMsgs());
+         cmsg->setControlInfo(cmd);
+         delete ind;
+         delete msg;
+         cmsg->setKind(SCTP_C_ABORT);
+         sendOrSchedule(cmsg);
+         break;
+      }
+      case SCTP_I_ESTABLISHED:
+      {
+         if (clientSocket.getState()==SCTPSocket::CONNECTING)
+            clientSocket.processMessage(PK(msg));
+         else
+         {
+            int32 count=0;
+            SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->removeControlInfo());
+            numSessions++;
+            serverAssocId = connectInfo->getAssocId();
+            id = serverAssocId;
+            outboundStreams = connectInfo->getOutboundStreams();
+            rcvdPacketsPerAssoc[serverAssocId]= (long) par("numPacketsToReceivePerClient");
+            sentPacketsPerAssoc[serverAssocId]= (long) par("numPacketsToSendPerClient");
+            char text[30];
+            sprintf(text, "App: Received Bytes of assoc %d",serverAssocId);
+            bytesPerAssoc[serverAssocId] = new cOutVector(text);
+            rcvdBytesPerAssoc[serverAssocId]= 0;
+            sprintf(text, "App: EndToEndDelay of assoc %d",serverAssocId);
+            endToEndDelay[serverAssocId] = new cOutVector(text);
+            sprintf(text, "Hist: EndToEndDelay of assoc %d",serverAssocId);
+            histEndToEndDelay[serverAssocId] = new cDoubleHistogram(text);
+
+            //delete connectInfo;
             delete msg;
-            cmsg->setKind(SCTP_C_ABORT);
-            sendOrSchedule(cmsg);
-            break;
-        }
-        case SCTP_I_ESTABLISHED:
-        {
-            if (clientSocket.getState()==SCTPSocket::CONNECTING)
-                clientSocket.processMessage(PK(msg));
-            else
+            if ((long) par("numPacketsToSendPerClient") > 0)
             {
-                int32 count=0;
-                SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->removeControlInfo());
-                numSessions++;
-                serverAssocId = connectInfo->getAssocId();
-                id = serverAssocId;
-                outboundStreams = connectInfo->getOutboundStreams();
-                rcvdPacketsPerAssoc[serverAssocId]= (long) par("numPacketsToReceivePerClient");
-                sentPacketsPerAssoc[serverAssocId]= (long) par("numPacketsToSendPerClient");
-                char text[30];
-                sprintf(text, "App: Received Bytes of assoc %d",serverAssocId);
-                bytesPerAssoc[serverAssocId] = new cOutVector(text);
-                rcvdBytesPerAssoc[serverAssocId]= 0;
-                sprintf(text, "App: EndToEndDelay of assoc %d",serverAssocId);
-                endToEndDelay[serverAssocId] = new cOutVector(text);
-                sprintf(text, "Hist: EndToEndDelay of assoc %d",serverAssocId);
-                histEndToEndDelay[serverAssocId] = new cDoubleHistogram(text);
-
-                //delete connectInfo;
-                delete msg;
-                if ((long) par("numPacketsToSendPerClient") > 0)
-                {
-                    SentPacketsPerAssoc::iterator i=sentPacketsPerAssoc.find(serverAssocId);
-                    numRequestsToSend = i->second;
-                    if ((simtime_t)par("thinkTime") > 0)
-                    {
+               SentPacketsPerAssoc::iterator i=sentPacketsPerAssoc.find(serverAssocId);
+               numRequestsToSend = i->second;
+               if ((simtime_t)par("thinkTime") > 0)
+               {
+                  generateAndSend(connectInfo);
+                  timeoutMsg->setKind(SCTP_C_SEND);
+                  scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
+                  numRequestsToSend--;
+                  i->second = numRequestsToSend;
+               }
+               else
+               {
+                  if (queueSize==0)
+                  {
+                     while (numRequestsToSend > 0)
+                     {
+                        generateAndSend(connectInfo);
+                        numRequestsToSend--;
+                        i->second = numRequestsToSend;
+                     }
+                  }
+                  else if (queueSize>0)
+                  {
+                     while (numRequestsToSend > 0 && count++ < queueSize*2)
+                     {
                         generateAndSend(connectInfo);
-                        timeoutMsg->setKind(SCTP_C_SEND);
-                        scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
                         numRequestsToSend--;
                         i->second = numRequestsToSend;
-                    }
-                    else
-                    {
-                        if (queueSize==0)
-                        {
-                            while (numRequestsToSend > 0)
-                            {
-                                generateAndSend(connectInfo);
-                                numRequestsToSend--;
-                                i->second = numRequestsToSend;
-                            }
-                        }
-                        else if (queueSize>0)
-                        {
-                            while (numRequestsToSend > 0 && count++ < queueSize*2)
-                            {
-                                generateAndSend(connectInfo);
-                                numRequestsToSend--;
-                                i->second = numRequestsToSend;
-                            }
-
-                            cPacket* cmsg = new cPacket("Queue");
-                            SCTPInfo* qinfo = new SCTPInfo();
-                            qinfo->setText(queueSize);
-                            cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
-                            qinfo->setAssocId(id);
-                            cmsg->setControlInfo(qinfo);
-                            sendOrSchedule(cmsg);
-                        }
-
-                        sctpEV3<<"!!!!!!!!!!!!!!!All data sent from Server !!!!!!!!!!\n";
-
-                        RcvdPacketsPerAssoc::iterator j=rcvdPacketsPerAssoc.find(serverAssocId);
-                        if (j->second == 0 && (simtime_t)par("waitToClose")>0)
-                        {
-                            char as[5];
-                            sprintf(as, "%d",serverAssocId);
-                            cMessage* abortMsg = new cMessage(as);
-                            abortMsg->setKind(SCTP_I_ABORT);
-                            scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), abortMsg);
-                        }
-                        else
-                        {
-                            sctpEV3<<"no more packets to send, call shutdown for assoc "<<serverAssocId<<"\n";
-                            cPacket* cmsg = new cPacket("ShutdownRequest");
-                            SCTPCommand* cmd = new SCTPCommand();
-                            cmsg->setKind(SCTP_C_SHUTDOWN);
-                            cmd->setAssocId(serverAssocId);
-                            cmsg->setControlInfo(cmd);
-                            sendOrSchedule(cmsg);
-                        }
-                    }
-                }
+                     }
+
+                     cPacket* cmsg = new cPacket("Queue");
+                     SCTPInfo* qinfo = new SCTPInfo();
+                     qinfo->setText(queueSize);
+                     cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
+                     qinfo->setAssocId(id);
+                     cmsg->setControlInfo(qinfo);
+                     sendOrSchedule(cmsg);
+                  }
+
+                  sctpEV3<<"!!!!!!!!!!!!!!!All data sent from Server !!!!!!!!!!\n";
+
+                  RcvdPacketsPerAssoc::iterator j=rcvdPacketsPerAssoc.find(serverAssocId);
+                  if (j->second == 0 && (simtime_t)par("waitToClose")>0)
+                  {
+                     char as[5];
+                     sprintf(as, "%d",serverAssocId);
+                     cMessage* abortMsg = new cMessage(as);
+                     abortMsg->setKind(SCTP_I_ABORT);
+                     scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), abortMsg);
+                  }
+                  else
+                  {
+                     sctpEV3<<"no more packets to send, call shutdown for assoc "<<serverAssocId<<"\n";
+                     cPacket* cmsg = new cPacket("ShutdownRequest");
+                     SCTPCommand* cmd = new SCTPCommand();
+                     cmsg->setKind(SCTP_C_SHUTDOWN);
+                     cmd->setAssocId(serverAssocId);
+                     cmsg->setControlInfo(cmd);
+                     sendOrSchedule(cmsg);
+                  }
+               }
             }
-            break;
-        }
-        case SCTP_I_DATA_NOTIFICATION:
-        {
-            notifications++;
-            SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
-            cPacket* cmsg = new cPacket("Notification");
-            SCTPSendCommand *cmd = new SCTPSendCommand();
-            id = ind->getAssocId();
-            cmd->setAssocId(id);
-            cmd->setSid(ind->getSid());
-            cmd->setNumMsgs(ind->getNumMsgs());
-            cmsg->setKind(SCTP_C_RECEIVE);
-            cmsg->setControlInfo(cmd);
-            delete ind;
-            delete msg;
-            if (!cmsg->isScheduled() && schedule==false)
+         }
+         break;
+      }
+      case SCTP_I_DATA_NOTIFICATION:
+      {
+         notifications++;
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         cPacket* cmsg = new cPacket("Notification");
+         SCTPSendCommand *cmd = new SCTPSendCommand();
+         id = ind->getAssocId();
+         cmd->setAssocId(id);
+         cmd->setSid(ind->getSid());
+         cmd->setNumMsgs(ind->getNumMsgs());
+         cmsg->setKind(SCTP_C_RECEIVE);
+         cmsg->setControlInfo(cmd);
+         delete ind;
+         delete msg;
+         if (!cmsg->isScheduled() && schedule==false)
+         {
+            scheduleAt(simulation.getSimTime()+(simtime_t)par("delayFirstRead"), cmsg);
+         }
+         else if (schedule==true)
+            sendOrSchedule(cmsg);
+         break;
+      }
+      case SCTP_I_DATA:
+      {
+         SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->getControlInfo());
+         id = ind->getAssocId();
+         RcvdBytesPerAssoc::iterator j=rcvdBytesPerAssoc.find(id);
+         if (j==rcvdBytesPerAssoc.end() && (clientSocket.getState()==SCTPSocket::CONNECTED))
+            clientSocket.processMessage(PK(msg));
+         else
+         {
+            j->second+= PK(msg)->getByteLength();
+            BytesPerAssoc::iterator k=bytesPerAssoc.find(id);
+            k->second->record(j->second);
+            packetsRcvd++;
+            if (echoFactor==0)
             {
-                scheduleAt(simulation.getSimTime()+(simtime_t)par("delayFirstRead"), cmsg);
+               if ((long)par("numPacketsToReceivePerClient")>0)
+               {
+                  RcvdPacketsPerAssoc::iterator i=rcvdPacketsPerAssoc.find(id);
+                  i->second--;
+                  SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg);
+                  EndToEndDelay::iterator j=endToEndDelay.find(id);
+                  j->second->record(simulation.getSimTime()-smsg->getCreationTime());
+                  HistEndToEndDelay::iterator k=histEndToEndDelay.find(id);
+                  k->second->collect(simulation.getSimTime()-smsg->getCreationTime());
+                  if (i->second == 0)
+                  {
+                     cPacket* cmsg = new cPacket("Request");
+                     SCTPInfo* qinfo = new SCTPInfo();
+                     cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+                     qinfo->setAssocId(id);
+                     cmsg->setControlInfo(qinfo);
+                     sendOrSchedule(cmsg);
+                  }
+               }
+               delete msg;
             }
-            else if (schedule==true)
-                sendOrSchedule(cmsg);
-            break;
-        }
-        case SCTP_I_DATA:
-        {
-            SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->getControlInfo());
-            id = ind->getAssocId();
-            RcvdBytesPerAssoc::iterator j=rcvdBytesPerAssoc.find(id);
-            if (j==rcvdBytesPerAssoc.end() && (clientSocket.getState()==SCTPSocket::CONNECTED))
-                clientSocket.processMessage(PK(msg));
             else
             {
-                j->second+= PK(msg)->getByteLength();
-                BytesPerAssoc::iterator k=bytesPerAssoc.find(id);
-                k->second->record(j->second);
-                packetsRcvd++;
-                if (echoFactor==0)
-                {
-                    if ((long)par("numPacketsToReceivePerClient")>0)
-                    {
-                        RcvdPacketsPerAssoc::iterator i=rcvdPacketsPerAssoc.find(id);
-                        i->second--;
-                        SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg);
-                        EndToEndDelay::iterator j=endToEndDelay.find(id);
-                        j->second->record(simulation.getSimTime()-smsg->getCreationTime());
-                        HistEndToEndDelay::iterator k=histEndToEndDelay.find(id);
-                        k->second->collect(simulation.getSimTime()-smsg->getCreationTime());
-                        if (i->second == 0)
-                        {
-                            cPacket* cmsg = new cPacket("Request");
-                            SCTPInfo* qinfo = new SCTPInfo();
-                            cmsg->setKind(SCTP_C_NO_OUTSTANDING);
-                            qinfo->setAssocId(id);
-                            cmsg->setControlInfo(qinfo);
-                            sendOrSchedule(cmsg);
-                        }
-                    }
-                    delete msg;
-                }
-                else
-                {
-                    SCTPSendCommand *cmd = new SCTPSendCommand();
-                    cmd->setAssocId(id);
-
-                    SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
-                    EndToEndDelay::iterator j=endToEndDelay.find(id);
-                    j->second->record(simulation.getSimTime()-smsg->getCreationTime());
-                    HistEndToEndDelay::iterator k=histEndToEndDelay.find(id);
-                    k->second->collect(simulation.getSimTime()-smsg->getCreationTime());
-                    cPacket* cmsg = new cPacket("SVData");
-                    bytesSent+=smsg->getByteLength();
-                    cmd->setSendUnordered(cmd->getSendUnordered());
-                    lastStream=(lastStream+1)%outboundStreams;
-                    cmd->setSid(lastStream);
-                    cmd->setLast(true);
-                    cmsg->encapsulate(smsg);
-                    cmsg->setKind(SCTP_C_SEND);
-                    cmsg->setControlInfo(cmd);
-                    packetsSent++;
-                    delete msg;
-                    sendOrSchedule(cmsg);
-                }
+               SCTPSendCommand *cmd = new SCTPSendCommand();
+               cmd->setAssocId(id);
+
+               SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
+               EndToEndDelay::iterator j=endToEndDelay.find(id);
+               j->second->record(simulation.getSimTime()-smsg->getCreationTime());
+               HistEndToEndDelay::iterator k=histEndToEndDelay.find(id);
+               k->second->collect(simulation.getSimTime()-smsg->getCreationTime());
+               cPacket* cmsg = new cPacket("SVData");
+               bytesSent+=smsg->getByteLength();
+               cmd->setSendUnordered(cmd->getSendUnordered());
+               cmd->setPrValue(0);
+               lastStream=(lastStream+1)%outboundStreams;
+               cmd->setSid(lastStream);
+               cmd->setLast(true);
+               cmsg->encapsulate(smsg);
+               cmsg->setKind(SCTP_C_SEND);
+               cmsg->setControlInfo(cmd);
+               packetsSent++;
+               delete msg;
+               sendOrSchedule(cmsg);
             }
-
-            break;
-        }
-        case SCTP_I_SHUTDOWN_RECEIVED:
-        {
-            SCTPCommand *command = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
-            id = command->getAssocId();
-            sctpEV3<<"server: SCTP_I_SHUTDOWN_RECEIVED for assoc "<<id<<"\n";
-            RcvdPacketsPerAssoc::iterator i=rcvdPacketsPerAssoc.find(id);
-            if (i==rcvdPacketsPerAssoc.end()&& (clientSocket.getState()==SCTPSocket::CONNECTED))
-                clientSocket.processMessage(PK(msg));
-            else
+         }
+
+         break;
+      }
+      case SCTP_I_SHUTDOWN_RECEIVED:
+      {
+         SCTPCommand *command = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         id = command->getAssocId();
+         sctpEV3<<"server: SCTP_I_SHUTDOWN_RECEIVED for assoc "<<id<<"\n";
+         RcvdPacketsPerAssoc::iterator i=rcvdPacketsPerAssoc.find(id);
+         if (i==rcvdPacketsPerAssoc.end()&& (clientSocket.getState()==SCTPSocket::CONNECTED))
+            clientSocket.processMessage(PK(msg));
+         else
+         {
+            if (i->second == 0)
             {
-                if (i->second == 0)
-                {
-                    cPacket* cmsg = new cPacket("Request");
-                    SCTPInfo* qinfo = new SCTPInfo();
-                    cmsg->setKind(SCTP_C_NO_OUTSTANDING);
-                    qinfo->setAssocId(id);
-                    cmsg->setControlInfo(qinfo);
-                    sendOrSchedule(cmsg);
-                }
-                delete command;
-                shutdownReceived = true;
+               cPacket* cmsg = new cPacket("Request");
+               SCTPInfo* qinfo = new SCTPInfo();
+               cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+               qinfo->setAssocId(id);
+               cmsg->setControlInfo(qinfo);
+               sendOrSchedule(cmsg);
             }
-            delete msg;
-        }
-        case SCTP_I_CLOSED: delete msg;
-        break;
-    }
-
-    if (ev.isGUI())
-    {
-        char buf[32];
-        RcvdBytesPerAssoc::iterator l=rcvdBytesPerAssoc.find(id);
-        sprintf(buf, "rcvd: %ld bytes\nsent: %ld bytes", l->second, bytesSent);
-        getDisplayString().setTagArg("t",0,buf);
-    }
+            delete command;
+            shutdownReceived = true;
+         }
+         delete msg;
+      }
+      case SCTP_I_SEND_STREAMS_RESETTED:
+      case SCTP_I_RCV_STREAMS_RESETTED:
+      {
+         ev<< "Streams have been resetted\n";
+         break;
+      }
+      case SCTP_I_CLOSED: delete msg;
+      break;
+   }
+
+   if (ev.isGUI())
+   {
+      char buf[32];
+      RcvdBytesPerAssoc::iterator l=rcvdBytesPerAssoc.find(id);
+      sprintf(buf, "rcvd: %ld bytes\nsent: %ld bytes", l->second, bytesSent);
+      getDisplayString().setTagArg("t",0,buf);
+   }
 }
 
 void SCTPPeer::handleTimer(cMessage *msg)
 {
-    cPacket* cmsg;
-    SCTPCommand* cmd;
-    int32 id;
-
-
-    sctpEV3<<"SCTPPeer::handleTimer\n";
-
-    SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->getControlInfo());
-    switch (msg->getKind())
-    {
-        case MSGKIND_CONNECT:
-            sctpEV3 << "starting session call connect\n";
-            connect();
-            break;
-        case SCTP_C_SEND:
-
-            if (numRequestsToSend>0)
-            {
-                generateAndSend(connectInfo);
-                if ((simtime_t)par("thinkTime") > 0)
-                    scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
-                numRequestsToSend--;
-            }
-            break;
-        case SCTP_I_ABORT:
-
-            cmsg = new cPacket("CLOSE", SCTP_C_CLOSE);
-            cmd = new SCTPCommand();
-            id = atoi(msg->getName());
-            cmd->setAssocId(id);
-            cmsg->setControlInfo(cmd);
-            sendOrSchedule(cmsg);
-            break;
-        case SCTP_C_RECEIVE:
-            schedule = true;
-            sendOrSchedule(PK(msg));
-            break;
-        default:
-
-            break;
-    }
+   cPacket* cmsg;
+   SCTPCommand* cmd;
+   int32 id;
+
+
+   sctpEV3<<"SCTPPeer::handleTimer\n";
+
+   SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->getControlInfo());
+   switch (msg->getKind())
+   {
+      case MSGKIND_CONNECT:
+         sctpEV3 << "starting session call connect\n";
+         connect();
+         break;
+      case SCTP_C_SEND:
+
+         if (numRequestsToSend>0)
+         {
+            generateAndSend(connectInfo);
+            if ((simtime_t)par("thinkTime") > 0)
+               scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
+            numRequestsToSend--;
+         }
+         break;
+      case SCTP_I_ABORT:
+
+         cmsg = new cPacket("CLOSE", SCTP_C_CLOSE);
+         cmd = new SCTPCommand();
+         id = atoi(msg->getName());
+         cmd->setAssocId(id);
+         cmsg->setControlInfo(cmd);
+         sendOrSchedule(cmsg);
+         break;
+      case SCTP_C_RECEIVE:
+         schedule = true;
+         sendOrSchedule(PK(msg));
+         break;
+      default:
+
+         break;
+   }
 }
 
 void SCTPPeer::socketDataNotificationArrived(int32 connId, void *ptr, cPacket *msg)
 {
-    SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
-    cPacket* cmsg = new cPacket("CMSG");
-    SCTPSendCommand *cmd = new SCTPSendCommand();
-    cmd->setAssocId(ind->getAssocId());
-    cmd->setSid(ind->getSid());
-    cmd->setNumMsgs(ind->getNumMsgs());
-    cmsg->setKind(SCTP_C_RECEIVE);
-    cmsg->setControlInfo(cmd);
-    delete ind;
-    clientSocket.sendNotification(cmsg);
+   SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+   cPacket* cmsg = new cPacket("CMSG");
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(ind->getAssocId());
+   cmd->setSid(ind->getSid());
+   cmd->setNumMsgs(ind->getNumMsgs());
+   cmsg->setKind(SCTP_C_RECEIVE);
+   cmsg->setControlInfo(cmd);
+   delete ind;
+   clientSocket.sendNotification(cmsg);
 }
 
 
 void SCTPPeer::socketPeerClosed(int32, void *)
 {
-    // close the connection (if not already closed)
-    if (clientSocket.getState()==SCTPSocket::PEER_CLOSED)
-    {
-        ev << "remote SCTP closed, closing here as well\n";
-        setStatusString("closing");
-        clientSocket.close();
-    }
+   // close the connection (if not already closed)
+   if (clientSocket.getState()==SCTPSocket::PEER_CLOSED)
+   {
+      ev << "remote SCTP closed, closing here as well\n";
+      setStatusString("closing");
+      clientSocket.close();
+   }
 }
 
 void SCTPPeer::socketClosed(int32, void *)
 {
-    // *redefine* to start another session etc.
-    ev << "connection closed\n";
-    setStatusString("closed");
+   // *redefine* to start another session etc.
+   ev << "connection closed\n";
+   setStatusString("closed");
 }
 
 void SCTPPeer::socketFailure(int32, void *, int32 code)
 {
-    // subclasses may override this function, and add code try to reconnect after a delay.
-    ev << "connection broken\n";
-    setStatusString("broken");
-    // reconnect after a delay
-    timeMsg->setKind(MSGKIND_CONNECT);
-    scheduleAt(simulation.getSimTime()+(simtime_t)par("reconnectInterval"), timeMsg);
+   // subclasses may override this function, and add code try to reconnect after a delay.
+   ev << "connection broken\n";
+   setStatusString("broken");
+   // reconnect after a delay
+   timeMsg->setKind(MSGKIND_CONNECT);
+   scheduleAt(simulation.getSimTime()+(simtime_t)par("reconnectInterval"), timeMsg);
 }
 
 void SCTPPeer::socketStatusArrived(int32 assocId, void *yourPtr, SCTPStatusInfo *status)
 {
 struct pathStatus ps;
-    SCTPPathStatus::iterator i=sctpPathStatus.find(status->getPathId());
-    if (i!=sctpPathStatus.end())
-    {
-        ps = i->second;
-        ps.active=status->getActive();
-    }
-    else
-    {
-        ps.active = status->getActive();
-        ps.primaryPath = false;
-        sctpPathStatus[ps.pid]=ps;
-    }
+   SCTPPathStatus::iterator i=sctpPathStatus.find(status->getPathId());
+   if (i!=sctpPathStatus.end())
+   {
+      ps = i->second;
+      ps.active=status->getActive();
+   }
+   else
+   {
+      ps.active = status->getActive();
+      ps.primaryPath = false;
+      sctpPathStatus[ps.pid]=ps;
+   }
 }
 
 void SCTPPeer::setStatusString(const char *s)
 {
-    if (ev.isGUI()) getDisplayString().setTagArg("t", 0, s);
+   if (ev.isGUI()) getDisplayString().setTagArg("t", 0, s);
 }
 
 void SCTPPeer::sendRequest(bool last)
 {
-    sctpEV3 << "sending request, " << numRequestsToSend-1 << " more to go\n";
-    long numBytes = par("requestLength");
-    if (numBytes < 1)
-        numBytes=1;
-
-    sctpEV3 << "SCTPClient: sending " << numBytes << " data bytes\n";
-
-    cPacket* cmsg = new cPacket("AppData");
-    SCTPSimpleMessage* msg=new SCTPSimpleMessage("data");
-
-    msg->setDataArraySize(numBytes);
-    for (int32 i=0; i<numBytes; i++)
-    {
-        msg->setData(i, 'a');
-    }
-    msg->setDataLen(numBytes);
-    msg->setBitLength(numBytes * 8);
-    msg->setCreationTime(simulation.getSimTime());
-    cmsg->encapsulate(msg);
-    if (ordered)
-        cmsg->setKind(SCTP_C_SEND_ORDERED);
-    else
-        cmsg->setKind(SCTP_C_SEND_UNORDERED);
-    // send SCTPMessage with SCTPSimpleMessage enclosed
-    clientSocket.send(cmsg, last);
-    bytesSent+=numBytes;
+   sctpEV3 << "sending request, " << numRequestsToSend-1 << " more to go\n";
+   long numBytes = par("requestLength");
+   if (numBytes < 1)
+      numBytes=1;
+
+   sctpEV3 << "SCTPClient: sending " << numBytes << " data bytes\n";
+
+   cPacket* cmsg = new cPacket("AppData");
+   SCTPSimpleMessage* msg=new SCTPSimpleMessage("data");
+
+   msg->setDataArraySize(numBytes);
+   for (int32 i=0; i<numBytes; i++)
+   {
+      msg->setData(i, 'a');
+   }
+   msg->setDataLen(numBytes);
+   msg->setEncaps(false);
+   msg->setBitLength(numBytes * 8);
+   msg->setCreationTime(simulation.getSimTime());
+   cmsg->encapsulate(msg);
+   if (ordered)
+      cmsg->setKind(SCTP_C_SEND_ORDERED);
+   else
+      cmsg->setKind(SCTP_C_SEND_UNORDERED);
+   // send SCTPMessage with SCTPSimpleMessage enclosed
+   clientSocket.send(cmsg, (int32)par("prMethod"),(double)par("prValue"), last);
+   bytesSent+=numBytes;
 }
 
 
 void SCTPPeer::socketEstablished(int32, void *)
 {
-    int32 count = 0;
-     // *redefine* to perform or schedule first sending
-    ev<<"SCTPClient: connected\n";
-    setStatusString("connected");
-    // determine number of requests in this session
-    numRequestsToSend = (long) par("numRequestsPerSession");
-    numPacketsToReceive = (long) par("numPacketsToReceive");
-    if (numRequestsToSend<1)
-        numRequestsToSend = 0;
-    // perform first request (next one will be sent when reply arrives)
-    if (numRequestsToSend>0)
-    {
-        if ((simtime_t)par("thinkTime") > 0)
-        {
-            if (sendAllowed)
-            {
-                sendRequest();
-                numRequestsToSend--;
-            }
-            timeMsg->setKind(MSGKIND_SEND);
-            scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeMsg);
-
-        }
-        else
-        {
-            if (queueSize>0)
-            {
-                while (numRequestsToSend > 0 && count++ < queueSize*2 && sendAllowed)
-                {
-                    if (count == queueSize*2)
-                        sendRequest();
-                    else
-                        sendRequest(false);
-                    numRequestsToSend--;
-                }
-                if (numRequestsToSend>0 && sendAllowed)
-                    sendQueueRequest();
-            }
-            else
-            {
-                while (numRequestsToSend > 0 && sendAllowed)
-                {
-                    sendRequest();
-                    numRequestsToSend--;
-                }
-            }
-
-            if (numPacketsToReceive == 0 && (simtime_t)par("waitToClose")>0)
+   int32 count = 0;
+    // *redefine* to perform or schedule first sending
+   ev<<"SCTPClient: connected\n";
+   setStatusString("connected");
+   // determine number of requests in this session
+   numRequestsToSend = (long) par("numRequestsPerSession");
+   numPacketsToReceive = (long) par("numPacketsToReceive");
+   if (numRequestsToSend<1)
+      numRequestsToSend = 0;
+   // perform first request (next one will be sent when reply arrives)
+   if (numRequestsToSend>0)
+   {
+      if ((simtime_t)par("thinkTime") > 0)
+      {
+         if (sendAllowed)
+         {
+            sendRequest();
+            numRequestsToSend--;
+         }
+         timeMsg->setKind(MSGKIND_SEND);
+         scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeMsg);
+
+      }
+      else
+      {
+         if (queueSize>0)
+         {
+            while (numRequestsToSend > 0 && count++ < queueSize*2 && sendAllowed)
             {
-                timeMsg->setKind(MSGKIND_ABORT);
-                scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), timeMsg);
+               if (count == queueSize*2)
+                  sendRequest();
+               else
+                  sendRequest(false);
+               numRequestsToSend--;
             }
-            if (numRequestsToSend == 0 && (simtime_t)par("waitToClose")==0)
+            if (numRequestsToSend>0 && sendAllowed)
+               sendQueueRequest();
+         }
+         else
+         {
+            while (numRequestsToSend > 0 && sendAllowed)
             {
-                sctpEV3<<"socketEstablished:no more packets to send, call shutdown\n";
-                clientSocket.shutdown();
+               sendRequest();
+               numRequestsToSend--;
             }
-        }
-    }
+         }
+
+         if (numPacketsToReceive == 0 && (simtime_t)par("waitToClose")>0)
+         {
+            timeMsg->setKind(MSGKIND_ABORT);
+            scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), timeMsg);
+         }
+         if (numRequestsToSend == 0 && (simtime_t)par("waitToClose")==0)
+         {
+            sctpEV3<<"socketEstablished:no more packets to send, call shutdown\n";
+            clientSocket.shutdown();
+         }
+      }
+   }
 }
 
 void SCTPPeer::sendQueueRequest()
 {
-    cPacket* cmsg = new cPacket("Queue");
-    SCTPInfo* qinfo = new SCTPInfo();
-    qinfo->setText(queueSize);
-    cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
-    qinfo->setAssocId(clientSocket.getConnectionId());
-    cmsg->setControlInfo(qinfo);
-    clientSocket.sendRequest(cmsg);
+   cPacket* cmsg = new cPacket("Queue");
+   SCTPInfo* qinfo = new SCTPInfo();
+   qinfo->setText(queueSize);
+   cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
+   qinfo->setAssocId(clientSocket.getConnectionId());
+   cmsg->setControlInfo(qinfo);
+   clientSocket.sendRequest(cmsg);
 
 }
 
@@ -609,113 +639,116 @@ void SCTPPeer::sendRequestArrived()
 {
 int32 count = 0;
 
-    sctpEV3<<"sendRequestArrived numRequestsToSend="<<numRequestsToSend<<"\n";
-    while (numRequestsToSend > 0 && count++ < queueSize && sendAllowed)
-    {
-        numRequestsToSend--;
-        if (count == queueSize || numRequestsToSend==0)
-            sendRequest();
-        else
-            sendRequest(false);
+   sctpEV3<<"sendRequestArrived numRequestsToSend="<<numRequestsToSend<<"\n";
+   while (numRequestsToSend > 0 && count++ < queueSize && sendAllowed)
+   {
+      numRequestsToSend--;
+      if (count == queueSize || numRequestsToSend==0)
+         sendRequest();
+      else
+         sendRequest(false);
 
-        if (numRequestsToSend == 0)
-        {
-            sctpEV3<<"no more packets to send, call shutdown\n";
-            clientSocket.shutdown();
-        }
-    }
+      if (numRequestsToSend == 0)
+      {
+         sctpEV3<<"no more packets to send, call shutdown\n";
+         clientSocket.shutdown();
+      }
+   }
 
 
 }
 
 void SCTPPeer::socketDataArrived(int32, void *, cPacket *msg, bool)
 {
-    // *redefine* to perform or schedule next sending
-    packetsRcvd++;
-
-    sctpEV3<<"Client received packet Nr "<<packetsRcvd<<" from SCTP\n";
-
-    SCTPCommand* ind = check_and_cast<SCTPCommand*>(msg->getControlInfo());
-
-    bytesRcvd+=msg->getByteLength();
-
-    if (echoFactor > 0)
-    {
-        SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
-        cPacket* cmsg = new cPacket("SVData");
-        echoedBytesSent+=smsg->getBitLength()/8;
-        cmsg->encapsulate(smsg);
-        if (ind->getSendUnordered())
-            cmsg->setKind(SCTP_C_SEND_UNORDERED);
-        else
-            cmsg->setKind(SCTP_C_SEND_ORDERED);
-        packetsSent++;
-        delete msg;
-        clientSocket.send(cmsg,1);
-    }
-    if ((long)par("numPacketsToReceive")>0)
-    {
-        numPacketsToReceive--;
-        if (numPacketsToReceive == 0)
-        {
-            setStatusString("closing");
-            clientSocket.close();
-        }
-    }
+   // *redefine* to perform or schedule next sending
+   packetsRcvd++;
+
+   sctpEV3<<"Client received packet Nr "<<packetsRcvd<<" from SCTP\n";
+
+   SCTPCommand* ind = check_and_cast<SCTPCommand*>(msg->getControlInfo());
+
+   bytesRcvd+=msg->getByteLength();
+
+   if (echoFactor > 0)
+   {
+      SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
+      cPacket* cmsg = new cPacket("SVData");
+      echoedBytesSent+=smsg->getBitLength()/8;
+      cmsg->encapsulate(smsg);
+      if (ind->getSendUnordered())
+         cmsg->setKind(SCTP_C_SEND_UNORDERED);
+      else
+         cmsg->setKind(SCTP_C_SEND_ORDERED);
+      packetsSent++;
+      delete msg;
+      clientSocket.send(cmsg, 0, 0, 1);
+   }
+   if ((long)par("numPacketsToReceive")>0)
+   {
+      numPacketsToReceive--;
+      if (numPacketsToReceive == 0)
+      {
+         setStatusString("closing");
+         clientSocket.close();
+      }
+   }
 }
 
 
-
 void SCTPPeer::shutdownReceivedArrived(int32 connId)
 {
-    if (numRequestsToSend==0)
-    {
-        cPacket* cmsg = new cPacket("Request");
-        SCTPInfo* qinfo = new SCTPInfo();
-        cmsg->setKind(SCTP_C_NO_OUTSTANDING);
-        qinfo->setAssocId(connId);
-        cmsg->setControlInfo(qinfo);
-        clientSocket.sendNotification(cmsg);
-    }
+   if (numRequestsToSend==0)
+   {
+      cPacket* cmsg = new cPacket("Request");
+      SCTPInfo* qinfo = new SCTPInfo();
+      cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+      qinfo->setAssocId(connId);
+      cmsg->setControlInfo(qinfo);
+      clientSocket.sendNotification(cmsg);
+   }
 }
 
 
+void SCTPPeer::msgAbandonedArrived(int32 assocId)
+{
+   chunksAbandoned++;
+}
+
 
 void SCTPPeer::sendqueueFullArrived(int32 assocId)
 {
-    sendAllowed = false;
+   sendAllowed = false;
 }
 
 
 void SCTPPeer::finish()
 {
-    delete timeoutMsg;
-    delete connectTimer;
-        ev << getFullPath() << ": opened " << numSessions << " sessions\n";
-    ev << getFullPath() << ": sent " << bytesSent << " bytes in " << packetsSent << " packets\n";
-    for (RcvdBytesPerAssoc::iterator l=rcvdBytesPerAssoc.begin(); l!=rcvdBytesPerAssoc.end(); l++)
-    {
-        ev << getFullPath() << ": received " << l->second << " bytes in assoc " << l->first<< "\n";
-    }
-    ev << getFullPath() << "Over all " << packetsRcvd << " packets received\n ";
-    ev << getFullPath() << "Over all " << notifications << " notifications received\n ";
-    for (BytesPerAssoc::iterator j = bytesPerAssoc.begin(); j!= bytesPerAssoc.end(); j++)
-    {
-        delete j->second;
-        bytesPerAssoc.erase(j);
-    }
-    for (EndToEndDelay::iterator k = endToEndDelay.begin(); k!= endToEndDelay.end(); k++)
-    {
-        delete k->second;
-        endToEndDelay.erase(k);
-    }
-    for (HistEndToEndDelay::iterator l = histEndToEndDelay.begin(); l!= histEndToEndDelay.end(); l++)
-    {
-        delete l->second;
-        histEndToEndDelay.erase(l);
-    }
-    rcvdPacketsPerAssoc.clear();
-    sentPacketsPerAssoc.clear();
-    rcvdBytesPerAssoc.clear();
+   delete timeoutMsg;
+   delete connectTimer;
+      ev << getFullPath() << ": opened " << numSessions << " sessions\n";
+   ev << getFullPath() << ": sent " << bytesSent << " bytes in " << packetsSent << " packets\n";
+   for (RcvdBytesPerAssoc::iterator l=rcvdBytesPerAssoc.begin(); l!=rcvdBytesPerAssoc.end(); l++)
+   {
+      ev << getFullPath() << ": received " << l->second << " bytes in assoc " << l->first<< "\n";
+   }
+   ev << getFullPath() << "Over all " << packetsRcvd << " packets received\n ";
+   ev << getFullPath() << "Over all " << notifications << " notifications received\n ";
+   for (BytesPerAssoc::iterator j = bytesPerAssoc.begin(); j!= bytesPerAssoc.end(); j++)
+   {
+      delete j->second;
+      bytesPerAssoc.erase(j);
+   }
+   for (EndToEndDelay::iterator k = endToEndDelay.begin(); k!= endToEndDelay.end(); k++)
+   {
+      delete k->second;
+      endToEndDelay.erase(k);
+   }
+   for (HistEndToEndDelay::iterator l = histEndToEndDelay.begin(); l!= histEndToEndDelay.end(); l++)
+   {
+      delete l->second;
+      histEndToEndDelay.erase(l);
+   }
+   rcvdPacketsPerAssoc.clear();
+   sentPacketsPerAssoc.clear();
+   rcvdBytesPerAssoc.clear();
 }
-
diff --git a/src/applications/sctpapp/SCTPPeer.h b/src/applications/sctpapp/SCTPPeer.h
index 331b2c8..079cf64 100644
--- a/src/applications/sctpapp/SCTPPeer.h
+++ b/src/applications/sctpapp/SCTPPeer.h
@@ -8,7 +8,7 @@
 //
 // 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
+// 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
@@ -25,93 +25,94 @@
 
 class INET_API SCTPPeer : public cSimpleModule, public SCTPSocket::CallbackInterface
 {
-    protected:
-        int32 notifications;
-        int32 serverAssocId;
-        int32 clientAssocId;
-        SCTPSocket clientSocket;
-        double delay;
-        double echoFactor;
-        bool schedule;
-        bool shutdownReceived;
-        long bytesSent;
-        int32 packetsSent;
-        int32 packetsRcvd;
-        int32 numSessions;
-        int32 numRequestsToSend; // requests to send in this session
-        bool ordered;
-        int32 queueSize;
-        cMessage *timeoutMsg;
-        int32 outboundStreams;
-        cMessage *timeMsg;
-        cMessage *connectTimer;
-        int32 bytesRcvd;
-        int32 echoedBytesSent;
-        int32 lastStream;
-        bool sendAllowed;
-        int32 numPacketsToReceive;
-        typedef std::map<int32,long> RcvdPacketsPerAssoc;
-        RcvdPacketsPerAssoc rcvdPacketsPerAssoc;
-        typedef std::map<int32,long> SentPacketsPerAssoc;
-        SentPacketsPerAssoc sentPacketsPerAssoc;
-        typedef std::map<int32,long> RcvdBytesPerAssoc;
-        RcvdBytesPerAssoc rcvdBytesPerAssoc;
-        typedef std::map<int32,cOutVector*> BytesPerAssoc;
-        BytesPerAssoc bytesPerAssoc;
-        typedef std::map<int32,cDoubleHistogram*> HistEndToEndDelay;
-        HistEndToEndDelay histEndToEndDelay;
-        typedef std::map<int32,cOutVector*> EndToEndDelay;
-        EndToEndDelay endToEndDelay;
-        void sendOrSchedule(cPacket *msg);
-        void sendRequest(bool last=true);
-        int32 ssn;
-    public:
-        struct pathStatus {
-            bool active;
-            bool primaryPath;
-            IPAddress  pid;
-        };
-        typedef std::map<IPvXAddress,pathStatus> SCTPPathStatus;
-        SCTPPathStatus sctpPathStatus;
-        void initialize();
-        void handleMessage(cMessage *msg);
-        void finish();
-        void handleTimer(cMessage *msg);
-        void generateAndSend(SCTPConnectInfo *connectInfo);
-        void connect();
+   protected:
+      int32 notifications;
+      int32 serverAssocId;
+      int32 clientAssocId;
+      SCTPSocket clientSocket;
+      double delay;
+      double echoFactor;
+      bool schedule;
+      bool shutdownReceived;
+      long bytesSent;
+      int32 packetsSent;
+      int32 packetsRcvd;
+      int32 numSessions;
+      int32 numRequestsToSend; // requests to send in this session
+      bool ordered;
+      int32 queueSize;
+      cMessage *timeoutMsg;
+      int32 outboundStreams;
+      cMessage *timeMsg;
+      cMessage *connectTimer;
+      int32 bytesRcvd;
+      int32 echoedBytesSent;
+      int32 lastStream;
+      bool sendAllowed;
+      int32 numPacketsToReceive;
+      int32 chunksAbandoned;
+      typedef std::map<int32,long> RcvdPacketsPerAssoc;
+      RcvdPacketsPerAssoc rcvdPacketsPerAssoc;
+      typedef std::map<int32,long> SentPacketsPerAssoc;
+      SentPacketsPerAssoc sentPacketsPerAssoc;
+      typedef std::map<int32,long> RcvdBytesPerAssoc;
+      RcvdBytesPerAssoc rcvdBytesPerAssoc;
+      typedef std::map<int32,cOutVector*> BytesPerAssoc;
+      BytesPerAssoc bytesPerAssoc;
+      typedef std::map<int32,cDoubleHistogram*> HistEndToEndDelay;
+      HistEndToEndDelay histEndToEndDelay;
+      typedef std::map<int32,cOutVector*> EndToEndDelay;
+      EndToEndDelay endToEndDelay;
+      void sendOrSchedule(cPacket *msg);
+      void sendRequest(bool last=true);
+      int32 ssn;
+   public:
+      struct pathStatus {
+         bool active;
+         bool primaryPath;
+         IPAddress  pid;
+      };
+      typedef std::map<IPvXAddress,pathStatus> SCTPPathStatus;
+      SCTPPathStatus sctpPathStatus;
+      void initialize();
+      void handleMessage(cMessage *msg);
+      void finish();
+      void handleTimer(cMessage *msg);
+      void generateAndSend(SCTPConnectInfo *connectInfo);
+      void connect();
 
-        /** Does nothing but update statistics/status. Redefine to perform or schedule first sending. */
-        void socketEstablished(int32 connId, void *yourPtr);
+      /** Does nothing but update statistics/status. Redefine to perform or schedule first sending. */
+      void socketEstablished(int32 connId, void *yourPtr);
 
-        /**
-        * Does nothing but update statistics/status. Redefine to perform or schedule next sending.
-        * Beware: this funcion deletes the incoming message, which might not be what you want.
-        */
-        void socketDataArrived(int32 connId, void *yourPtr, cPacket *msg, bool urgent);
+      /**
+      * Does nothing but update statistics/status. Redefine to perform or schedule next sending.
+      * Beware: this funcion deletes the incoming message, which might not be what you want.
+      */
+      void socketDataArrived(int32 connId, void *yourPtr, cPacket *msg, bool urgent);
 
-        void socketDataNotificationArrived(int32 connId, void *yourPtr, cPacket *msg);
-        /** Since remote SCTP closed, invokes close(). Redefine if you want to do something else. */
-        void socketPeerClosed(int32 connId, void *yourPtr);
+      void socketDataNotificationArrived(int32 connId, void *yourPtr, cPacket *msg);
+      /** Since remote SCTP closed, invokes close(). Redefine if you want to do something else. */
+      void socketPeerClosed(int32 connId, void *yourPtr);
 
-        /** Does nothing but update statistics/status. Redefine if you want to do something else, such as opening a new connection. */
-        void socketClosed(int32 connId, void *yourPtr);
+      /** Does nothing but update statistics/status. Redefine if you want to do something else, such as opening a new connection. */
+      void socketClosed(int32 connId, void *yourPtr);
 
-        /** Does nothing but update statistics/status. Redefine if you want to try reconnecting after a delay. */
-        void socketFailure(int32 connId, void *yourPtr, int32 code);
+      /** Does nothing but update statistics/status. Redefine if you want to try reconnecting after a delay. */
+      void socketFailure(int32 connId, void *yourPtr, int32 code);
 
-        /** Redefine to handle incoming SCTPStatusInfo. */
-        void socketStatusArrived(int32 connId, void *yourPtr, SCTPStatusInfo *status);
-        //@}
-        void setPrimaryPath ();
-        void sendRequestArrived();
-        void sendQueueRequest();
-        void shutdownReceivedArrived(int32 connId);
-        void sendqueueFullArrived(int32 connId);
+      /** Redefine to handle incoming SCTPStatusInfo. */
+      void socketStatusArrived(int32 connId, void *yourPtr, SCTPStatusInfo *status);
+      //@}
+      void msgAbandonedArrived(int32 assocId);
+      void sendStreamResetNotification();
+      void setPrimaryPath ();
+      void sendRequestArrived();
+      void sendQueueRequest();
+      void shutdownReceivedArrived(int32 connId);
+      void sendqueueFullArrived(int32 connId);
 
-        void setStatusString(const char *s);
-        void addressAddedArrived(int32 assocId, IPvXAddress remoteAddr);
+      void setStatusString(const char *s);
+      void addressAddedArrived(int32 assocId, IPvXAddress remoteAddr);
 };
 
 #endif
-
-
diff --git a/src/applications/sctpapp/SCTPPeer.ned b/src/applications/sctpapp/SCTPPeer.ned
index 569b446..f3eb677 100644
--- a/src/applications/sctpapp/SCTPPeer.ned
+++ b/src/applications/sctpapp/SCTPPeer.ned
@@ -8,7 +8,7 @@
 //
 // 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
+// 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
@@ -19,28 +19,30 @@ package inet.applications.sctpapp;
 
 simple SCTPPeer like SCTPApp
 {
-    parameters:
-        string address = default(""); // may be left empty ("")
-        int port = default(0); // port number to listen on
-        int echoFactor = default(0);
-        double echoDelay @unit(s) = default(0s);
-        double delayFirstRead @unit(s) = default(0s);
-        int numPacketsToSendPerClient = default(0); // number of requests sent per session
-        int numPacketsToReceivePerClient = default(1);
-        int requestLength = default(1452); // length of a request (bytes)
-        double thinkTime @unit(s) = default(0s); // time gap between requests
-        double waitToClose @unit(s) = default(0s); //time to wait between last message sent and abort
-        bool ordered = default(true);
-        int outboundStreams = default(1);
-        int queueSize = default(0);
-        string connectAddress;  // server address (may be symbolic)
-        int connectPort; // port number to connect to
-        double startTime @unit(s) = default(1s); // time first session begins
-        int numRequestsPerSession = default(1);  // number of requests sent per session
-        int numPacketsToReceive = default(0);
-        @display("i=block/app");
-    gates:
-        input sctpIn @labels(SCTPCommand/up);
-        output sctpOut @labels(SCTPCommand/down);
+   parameters:
+      string address = default(""); // may be left empty ("")
+      int port = default(0); // port number to listen on
+      int echoFactor = default(0);
+      double echoDelay @unit(s) = default(0s);
+      double delayFirstRead @unit(s) = default(0s);
+      int numPacketsToSendPerClient = default(0);  // number of requests sent per session
+      int numPacketsToReceivePerClient = default(1);
+      int requestLength = default(1452); // length of a request (bytes)
+      double thinkTime @unit(s) = default(0s); // time gap between requests
+      double waitToClose @unit(s) = default(0s); //time to wait between last message sent and abort
+      bool ordered = default(true);
+      int outboundStreams = default(1);
+      int queueSize = default(0);
+      int prMethod = default(0);  //0=NONE, 1=PR_TTL, 2=PR_RTX, 3=PR_PRIO, 4=PR_STRRST
+      bool streamReset = default(false);
+      double prValue = default(0); //for PR-SCTP
+      string connectAddress;  // server address (may be symbolic)
+      int connectPort; // port number to connect to
+      double startTime @unit(s) = default(1s); // time first session begins
+      int numRequestsPerSession = default(1);  // number of requests sent per session
+      int numPacketsToReceive = default(0);
+      string streamPriorities = default("");
+   gates:
+      input sctpIn;
+      output sctpOut;
 }
-
diff --git a/src/applications/sctpapp/SCTPServer.cc b/src/applications/sctpapp/SCTPServer.cc
index efbfa0c..2613ebb 100644
--- a/src/applications/sctpapp/SCTPServer.cc
+++ b/src/applications/sctpapp/SCTPServer.cc
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2009 Thomas Dreibholz
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -26,542 +26,562 @@
 #include "SCTPAssociation.h"
 
 #define MSGKIND_CONNECT  0
-#define MSGKIND_SEND         1
-#define MSGKIND_      2
+#define MSGKIND_SEND     1
+#define MSGKIND_    2
 
 Define_Module(SCTPServer);
 
 void SCTPServer::initialize()
 {
-    char * token;
-    cPar *delT;
-    AddressVector addresses;
-    socket = NULL;
-    sctpEV3<<"initialize SCTP Server\n";
-    numSessions = packetsSent = packetsRcvd = bytesSent = notifications = 0;
-    WATCH(numSessions);
-    WATCH(packetsSent);
-    WATCH(packetsRcvd);
-    WATCH(bytesSent);
-    WATCH(numRequestsToSend);
-    // parameters
-    finishEndsSimulation = (bool)par("finishEndsSimulation");
-    const char* address = par("address");
-    token = strtok((char*)address,",");
-    while (token != NULL)
-    {
-        addresses.push_back(IPvXAddress(token));
-        token = strtok(NULL, ",");
-    }
-    int32 port = par("port");
-    echoFactor = par("echoFactor");
-    delay = par("echoDelay");
-    delayFirstRead = par("delayFirstRead");
-    delT = &par("readingInterval");
-    if (delT->isNumeric() && (double)*delT==0)
-        readInt=false;
-    else
-        readInt=true;
-    int32 messagesToPush = par("messagesToPush");
-    inboundStreams = par("inboundStreams");
-    outboundStreams = par("outboundStreams");
-    ordered = (bool)par("ordered");
-    queueSize = par("queueSize");
-    lastStream = 0;
-    //abort = NULL;
-    //abortSent = false;
-    timeoutMsg = new cMessage("SrvAppTimer");
-    delayTimer = new cMessage("delayTimer");
-    delayTimer->setContextPointer(this);
-    delayFirstReadTimer = new cMessage("delayFirstReadTimer");
-    firstData = true;
-    SCTPSocket *socket = new SCTPSocket();
-    socket->setOutputGate(gate("sctpOut"));
-    socket->setInboundStreams(inboundStreams);
-    socket->setOutboundStreams(outboundStreams);
-    if (strcmp(address,"")==0)
-        socket->bind(port);
-    else
-    {
-        socket->bindx(addresses, port);
-    }
-    socket->listen(true, par("numPacketsToSendPerClient"), messagesToPush);
-    sctpEV3<<"SCTPServer::initialized listen port="<<port<<"\n";
-    schedule = false;
-    shutdownReceived = false;
+   char * token;
+   cPar *delT;
+   AddressVector addresses;
+   socket = NULL;
+   sctpEV3<<"initialize SCTP Server\n";
+   numSessions = packetsSent = packetsRcvd = bytesSent = notifications = 0;
+   WATCH(numSessions);
+   WATCH(packetsSent);
+   WATCH(packetsRcvd);
+   WATCH(bytesSent);
+   WATCH(numRequestsToSend);
+   // parameters
+   finishEndsSimulation = (bool)par("finishEndsSimulation");
+   const char* address = par("address");
+   token = strtok((char*)address,",");
+   while (token != NULL)
+   {
+      addresses.push_back(IPvXAddress(token));
+      token = strtok(NULL, ",");
+   }
+   int32 port = par("port");
+   echoFactor = par("echoFactor");
+   delay = par("echoDelay");
+   delayFirstRead = par("delayFirstRead");
+   delT = &par("readingInterval");
+   if (delT->isNumeric() && (double)*delT==0)
+      readInt=false;
+   else
+      readInt=true;
+   int32 messagesToPush = par("messagesToPush");
+   inboundStreams = par("inboundStreams");
+   outboundStreams = par("outboundStreams");
+   ordered = (bool)par("ordered");
+   queueSize = par("queueSize");
+   lastStream = 0;
+   //abort = NULL;
+   //abortSent = false;
+   timeoutMsg = new cMessage("SrvAppTimer");
+   delayTimer = new cMessage("delayTimer");
+   delayTimer->setContextPointer(this);
+   delayFirstReadTimer = new cMessage("delayFirstReadTimer");
+   firstData = true;
+   SCTPSocket *socket = new SCTPSocket();
+   socket->setOutputGate(gate("sctpOut"));
+   socket->setInboundStreams(inboundStreams);
+   socket->setOutboundStreams(outboundStreams);
+   if (strcmp(address,"")==0)
+      socket->bind(port);
+   else
+   {
+      socket->bindx(addresses, port);
+   }
+   socket->listen(true,(bool)par("streamReset"), par("numPacketsToSendPerClient"), messagesToPush);
+   sctpEV3<<"SCTPServer::initialized listen port="<<port<<"\n";
+   schedule = false;
+   shutdownReceived = false;
+   uint32 streamNum = 0;
+   cStringTokenizer tokenizer(par("streamPriorities").stringValue());
+   while (tokenizer.hasMoreTokens())
+   {
+      const char *token = tokenizer.nextToken();
+      socket->setStreamPriority(streamNum, (uint32) atoi(token));
+
+      streamNum++;
+   }
 }
 
 void SCTPServer::sendOrSchedule(cPacket *msg)
 {
-    if (delay==0)
-    {
-        send(msg, "sctpOut");
-    }
-    else
-    {
-        scheduleAt(simulation.getSimTime()+delay, msg);
-    }
+   if (delay==0)
+   {
+      send(msg, "sctpOut");
+   }
+   else
+   {
+      scheduleAt(simulation.getSimTime()+delay, msg);
+   }
 }
 
 void SCTPServer::generateAndSend()
 {
-uint32 numBytes;
-
-    cPacket* cmsg = new cPacket("CMSG");
-    SCTPSimpleMessage* msg = new SCTPSimpleMessage("Server");
-    numBytes = (uint32)par("requestLength");
-    msg->setDataArraySize(numBytes);
-    for (uint32 i=0; i<numBytes; i++)
-    {
-        msg->setData(i, 's');
-    }
-    msg->setDataLen(numBytes);
-    msg->setBitLength(numBytes * 8);
-    cmsg->encapsulate(msg);
-    SCTPSendCommand *cmd = new SCTPSendCommand("Send1");
-    cmd->setAssocId(assocId);
-    if (ordered)
-        cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
-    else
-        cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
-    lastStream=(lastStream+1)%outboundStreams;
-    cmd->setSid(lastStream);
-    if (queueSize>0 && numRequestsToSend > 0 && count < queueSize*2)
-        cmd->setLast(false);
-    else
-        cmd->setLast(true);
-    cmsg->setKind(SCTP_C_SEND);
-    cmsg->setControlInfo(cmd);
-    packetsSent++;
-    bytesSent+=msg->getBitLength()/8;
-    sendOrSchedule(cmsg);
+   uint32 numBytes;
+
+   cPacket* cmsg = new cPacket("CMSG");
+   SCTPSimpleMessage* msg=new SCTPSimpleMessage("Server");
+   numBytes=(long uint32)par("requestLength");
+   msg->setDataArraySize(numBytes);
+   for (uint32 i=0; i<numBytes; i++)
+   {
+      msg->setData(i, 's');
+   }
+   msg->setDataLen(numBytes);
+   msg->setEncaps(false);
+   msg->setBitLength(numBytes * 8);
+   cmsg->encapsulate(msg);
+   SCTPSendCommand *cmd = new SCTPSendCommand("Send1");
+   cmd->setAssocId(assocId);
+   if (ordered)
+      cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
+   else
+      cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
+   lastStream=(lastStream+1)%outboundStreams;
+   cmd->setSid(lastStream);
+   cmd->setPrValue(par("prValue"));
+   cmd->setPrMethod((int32)par("prMethod"));
+   if (queueSize>0 && numRequestsToSend > 0 && count < queueSize*2)
+      cmd->setLast(false);
+   else
+      cmd->setLast(true);
+   cmsg->setKind(SCTP_C_SEND);
+   cmsg->setControlInfo(cmd);
+   packetsSent++;
+   bytesSent+=msg->getBitLength()/8;
+   sendOrSchedule(cmsg);
 }
 
 cPacket* SCTPServer::makeReceiveRequest(cPacket* msg)
 {
-    SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
-    cPacket* cmsg = new cPacket("ReceiveRequest");
-    SCTPSendCommand *cmd = new SCTPSendCommand("Send2");
-    cmd->setAssocId(ind->getAssocId());
-    cmd->setSid(ind->getSid());
-    cmd->setNumMsgs(ind->getNumMsgs());
-    cmsg->setKind(SCTP_C_RECEIVE);
-    cmsg->setControlInfo(cmd);
-    delete ind;
-    return cmsg;
+   SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+   cPacket* cmsg = new cPacket("ReceiveRequest");
+   SCTPSendCommand *cmd = new SCTPSendCommand("Send2");
+   cmd->setAssocId(ind->getAssocId());
+   cmd->setSid(ind->getSid());
+   cmd->setNumMsgs(ind->getNumMsgs());
+   cmsg->setKind(SCTP_C_RECEIVE);
+   cmsg->setControlInfo(cmd);
+   delete ind;
+   return cmsg;
 }
 
 cPacket* SCTPServer::makeDefaultReceive()
 {
-    cPacket* cmsg = new cPacket("DefaultReceive");
-    SCTPSendCommand *cmd = new SCTPSendCommand("Send3");
-    cmd->setAssocId(assocId);
-    cmd->setSid(0);
-    cmd->setNumMsgs(1);
-    cmsg->setKind(SCTP_C_RECEIVE);
-    cmsg->setControlInfo(cmd);
-    return cmsg;
+   cPacket* cmsg = new cPacket("DefaultReceive");
+   SCTPSendCommand *cmd = new SCTPSendCommand("Send3");
+   cmd->setAssocId(assocId);
+   cmd->setSid(0);
+   cmd->setNumMsgs(1);
+   cmsg->setKind(SCTP_C_RECEIVE);
+   cmsg->setControlInfo(cmd);
+   return cmsg;
 }
 
 cPacket* SCTPServer::makeAbortNotification(SCTPCommand* msg)
 {
-    SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg);
-    cPacket* cmsg = new cPacket("AbortNotification");
-    SCTPSendCommand *cmd = new SCTPSendCommand("Send4");
-    assocId = ind->getAssocId();
-    cmd->setAssocId(assocId);
-    cmd->setSid(ind->getSid());
-    cmd->setNumMsgs(ind->getNumMsgs());
-    cmsg->setControlInfo(cmd);
-    delete ind;
-    //delete msg;
-    cmsg->setKind(SCTP_C_ABORT);
-    return cmsg;
+   SCTPCommand *ind = check_and_cast<SCTPCommand *>(msg);
+   cPacket* cmsg = new cPacket("AbortNotification");
+   SCTPSendCommand *cmd = new SCTPSendCommand("Send4");
+   assocId = ind->getAssocId();
+   cmd->setAssocId(assocId);
+   cmd->setSid(ind->getSid());
+   cmd->setNumMsgs(ind->getNumMsgs());
+   cmsg->setControlInfo(cmd);
+   delete ind;
+   //delete msg;
+   cmsg->setKind(SCTP_C_ABORT);
+   return cmsg;
 }
 
 void SCTPServer::handleMessage(cMessage *msg)
 {
-    int32 id;
-    cPacket* cmsg;
-
-    if (msg->isSelfMessage())
-    {
-
-        handleTimer(msg);
-    }
-    else
-    {
-    switch (msg->getKind())
-    {
-        case SCTP_I_PEER_CLOSED:
-        case SCTP_I_ABORT:
-        {
-            SCTPCommand *command = dynamic_cast<SCTPCommand *>(msg->removeControlInfo());
-            assocId = command->getAssocId();
-            serverAssocStatMap[assocId].peerClosed = true;
-            if ((long) par("numPacketsToReceivePerClient")==0)
+   int32 id;
+   cPacket* cmsg;
+
+   if (msg->isSelfMessage())
+   {
+
+      handleTimer(msg);
+   }
+   else
+   {
+   switch (msg->getKind())
+   {
+      case SCTP_I_PEER_CLOSED:
+      case SCTP_I_ABORT:
+      {
+         SCTPCommand *command = dynamic_cast<SCTPCommand *>(msg->removeControlInfo());
+         assocId = command->getAssocId();
+         serverAssocStatMap[assocId].peerClosed = true;
+         if ((long) par("numPacketsToReceivePerClient")==0)
+         {
+            if (serverAssocStatMap[assocId].abortSent==false)
             {
-                if (serverAssocStatMap[assocId].abortSent==false)
-                {
-                    sendOrSchedule(makeAbortNotification(command->dup()));
-                    serverAssocStatMap[assocId].abortSent = true;
-                }
+               sendOrSchedule(makeAbortNotification(command->dup()));
+               serverAssocStatMap[assocId].abortSent = true;
             }
-            else
+         }
+         else
+         {
+            if (serverAssocStatMap[assocId].rcvdPackets==(unsigned long) par("numPacketsToReceivePerClient") &&
+               serverAssocStatMap[assocId].abortSent==false)
             {
-                if (serverAssocStatMap[assocId].rcvdPackets==(unsigned long) par("numPacketsToReceivePerClient") &&
-                    serverAssocStatMap[assocId].abortSent==false)
-                {
-                    sendOrSchedule(makeAbortNotification(command->dup()));
-                    serverAssocStatMap[assocId].abortSent = true;
-                }
+               sendOrSchedule(makeAbortNotification(command->dup()));
+               serverAssocStatMap[assocId].abortSent = true;
             }
-            if (delayTimer->isScheduled())
-                cancelEvent(delayTimer);
-            if (delayFirstReadTimer->isScheduled())
-                cancelEvent(delayFirstReadTimer);
-            delete command;
-            delete msg;
-            break;
-        }
-        case SCTP_I_ESTABLISHED:
-        {
-            count=0;
-            SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->removeControlInfo());
-            numSessions++;
-            assocId = connectInfo->getAssocId();
-            inboundStreams = connectInfo->getInboundStreams();
-            outboundStreams = connectInfo->getOutboundStreams();
-            serverAssocStatMap[assocId].rcvdPackets= (long) par("numPacketsToReceivePerClient");
-            serverAssocStatMap[assocId].sentPackets= (long) par("numPacketsToSendPerClient");
-            serverAssocStatMap[assocId].rcvdBytes=0;
-            serverAssocStatMap[assocId].start=0;
-            serverAssocStatMap[assocId].stop=0;
-            serverAssocStatMap[assocId].lifeTime=0;
-            serverAssocStatMap[assocId].abortSent=false;
-            serverAssocStatMap[assocId].peerClosed = false;
-            char text[30];
-            sprintf(text, "App: Received Bytes of assoc %d",assocId);
-            bytesPerAssoc[assocId] = new cOutVector(text);
-            sprintf(text, "App: EndToEndDelay of assoc %d",assocId);
-            endToEndDelay[assocId] = new cOutVector(text);
-
-            delete connectInfo;
-            delete msg;
-            if ((long) par("numPacketsToSendPerClient") > 0)
+         }
+         if (delayTimer->isScheduled())
+            cancelEvent(delayTimer);
+         if (delayFirstReadTimer->isScheduled())
+            cancelEvent(delayFirstReadTimer);
+        // delete command;
+         delete msg;
+         break;
+      }
+      case SCTP_I_ESTABLISHED:
+      {
+         count=0;
+         SCTPConnectInfo *connectInfo = dynamic_cast<SCTPConnectInfo *>(msg->removeControlInfo());
+         numSessions++;
+         assocId = connectInfo->getAssocId();
+         inboundStreams = connectInfo->getInboundStreams();
+         outboundStreams = connectInfo->getOutboundStreams();
+         serverAssocStatMap[assocId].rcvdPackets= (long) par("numPacketsToReceivePerClient");
+         serverAssocStatMap[assocId].sentPackets= (long) par("numPacketsToSendPerClient");
+         serverAssocStatMap[assocId].rcvdBytes=0;
+         serverAssocStatMap[assocId].start=0;
+         serverAssocStatMap[assocId].stop=0;
+         serverAssocStatMap[assocId].lifeTime=0;
+         serverAssocStatMap[assocId].abortSent=false;
+         serverAssocStatMap[assocId].peerClosed = false;
+         char text[30];
+         sprintf(text, "App: Received Bytes of assoc %d",assocId);
+         bytesPerAssoc[assocId] = new cOutVector(text);
+         sprintf(text, "App: EndToEndDelay of assoc %d",assocId);
+         endToEndDelay[assocId] = new cOutVector(text);
+
+         delete connectInfo;
+         delete msg;
+         if ((long) par("numPacketsToSendPerClient") > 0)
+         {
+            ServerAssocStatMap::iterator i = serverAssocStatMap.find(assocId);
+            numRequestsToSend = i->second.sentPackets;
+            if ((simtime_t)par("thinkTime") > 0)
             {
-                ServerAssocStatMap::iterator i = serverAssocStatMap.find(assocId);
-                numRequestsToSend = i->second.sentPackets;
-                if ((simtime_t)par("thinkTime") > 0)
-                {
-                    generateAndSend();
-                    timeoutMsg->setKind(SCTP_C_SEND);
-                    scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
-                    numRequestsToSend--;
-                    i->second.sentPackets = numRequestsToSend;
-                }
-                else
-                {
-                    if (queueSize==0)
-                    {
-                        while (numRequestsToSend > 0)
-                        {
-                            generateAndSend();
-                            numRequestsToSend--;
-                            i->second.sentPackets = numRequestsToSend;
-                        }
-                    }
-                    else if (queueSize>0)
-                    {
-                        while (numRequestsToSend > 0 && count++ < queueSize*2)
-                        {
-                            generateAndSend();
-                            numRequestsToSend--;
-                            i->second.sentPackets = numRequestsToSend;
-                        }
-
-                        cPacket* cmsg = new cPacket("Queue");
-                        SCTPInfo* qinfo = new SCTPInfo("Info1");
-                        qinfo->setText(queueSize);
-                        cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
-                        qinfo->setAssocId(id);
-                        cmsg->setControlInfo(qinfo);
-                        sendOrSchedule(cmsg);
-                    }
-                    ServerAssocStatMap::iterator j=serverAssocStatMap.find(assocId);
-                    if (j->second.rcvdPackets == 0 && (simtime_t)par("waitToClose")>0)
-                    {
-                        char as[5];
-                        sprintf(as, "%d",assocId);
-                        cPacket* abortMsg = new cPacket(as);
-                        abortMsg->setKind(SCTP_I_ABORT);
-                        scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), abortMsg);
-                    }
-                    else
-                    {
-                        sctpEV3<<"no more packets to send, call shutdown for assoc "<<assocId<<"\n";
-                        cPacket* cmsg = new cPacket("ShutdownRequest");
-                        SCTPCommand* cmd = new SCTPCommand("Send5");
-                        cmsg->setKind(SCTP_C_SHUTDOWN);
-                        cmd->setAssocId(assocId);
-                        cmsg->setControlInfo(cmd);
-                        sendOrSchedule(cmsg);
-                    }
-                }
+               generateAndSend();
+               timeoutMsg->setKind(SCTP_C_SEND);
+               scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
+               numRequestsToSend--;
+               i->second.sentPackets = numRequestsToSend;
             }
-            break;
-        }
-        case SCTP_I_DATA_NOTIFICATION:
-        {
-            notifications++;
+            else
+            {
+               if (queueSize==0)
+               {
+                  while (numRequestsToSend > 0)
+                  {
+                     generateAndSend();
+                     numRequestsToSend--;
+                     i->second.sentPackets = numRequestsToSend;
+                  }
+               }
+               else if (queueSize>0)
+               {
+                  while (numRequestsToSend > 0 && count++ < queueSize*2)
+                  {
+                     generateAndSend();
+                     numRequestsToSend--;
+                     i->second.sentPackets = numRequestsToSend;
+                  }
+
+                  cPacket* cmsg = new cPacket("Queue");
+                  SCTPInfo* qinfo = new SCTPInfo("Info1");
+                  qinfo->setText(queueSize);
+                  cmsg->setKind(SCTP_C_QUEUE_MSGS_LIMIT);
+                  qinfo->setAssocId(id);
+                  cmsg->setControlInfo(qinfo);
+                  sendOrSchedule(cmsg);
+               }
+               ServerAssocStatMap::iterator j=serverAssocStatMap.find(assocId);
+               if (j->second.rcvdPackets == 0 && (simtime_t)par("waitToClose")>0)
+               {
+                  char as[5];
+                  sprintf(as, "%d",assocId);
+                  cPacket* abortMsg = new cPacket(as);
+                  abortMsg->setKind(SCTP_I_ABORT);
+                  scheduleAt(simulation.getSimTime()+(simtime_t)par("waitToClose"), abortMsg);
+               }
+               else
+               {
+                  sctpEV3<<"no more packets to send, call shutdown for assoc "<<assocId<<"\n";
+                  cPacket* cmsg = new cPacket("ShutdownRequest");
+                  SCTPCommand* cmd = new SCTPCommand("Send5");
+                  cmsg->setKind(SCTP_C_SHUTDOWN);
+                  cmd->setAssocId(assocId);
+                  cmsg->setControlInfo(cmd);
+                  sendOrSchedule(cmsg);
+               }
+            }
+         }
+         break;
+      }
+      case SCTP_I_DATA_NOTIFICATION:
+      {
+         notifications++;
 
 
-            if (schedule==false)
+         if (schedule==false)
+         {
+            if (delayFirstRead>0 && !delayFirstReadTimer->isScheduled())
             {
-                if (delayFirstRead>0 && !delayFirstReadTimer->isScheduled())
-                {
-
-                    cmsg=makeReceiveRequest(PK(msg));
-                    scheduleAt(simulation.getSimTime()+delayFirstRead, cmsg);
-                    scheduleAt(simulation.getSimTime()+delayFirstRead, delayFirstReadTimer);
-                }
-                else if (readInt && firstData)
-                {
-                    firstData=false;
-                    cmsg=makeReceiveRequest(PK(msg));
-                    scheduleAt(simulation.getSimTime()+(simtime_t)par("readingInterval"), delayTimer);
-                    sendOrSchedule(cmsg);
-                }
-                else if (delayFirstRead==0 && readInt==false)
-                {
-                    cmsg=makeReceiveRequest(PK(msg));
-                    sendOrSchedule(cmsg);
-                }
 
+               cmsg=makeReceiveRequest(PK(msg));
+               scheduleAt(simulation.getSimTime()+delayFirstRead, cmsg);
+               scheduleAt(simulation.getSimTime()+delayFirstRead, delayFirstReadTimer);
             }
-            else
+            else if (readInt && firstData)
             {
-                sctpEV3<<simulation.getSimTime()<<" makeReceiveRequest\n";
-                cmsg=makeReceiveRequest(PK(msg));
-                sendOrSchedule(cmsg);
+               firstData=false;
+               cmsg=makeReceiveRequest(PK(msg));
+               scheduleAt(simulation.getSimTime()+(simtime_t)par("readingInterval"), delayTimer);
+               sendOrSchedule(cmsg);
             }
-            delete msg;
-            break;
-        }
-        case SCTP_I_DATA:
-        {
-            notifications--;
-            packetsRcvd++;
-            sctpEV3<<simulation.getSimTime()<<" server: data arrived. "<<packetsRcvd<<" Packets received now\n";
-            SCTPRcvCommand *ind = check_and_cast<SCTPRcvCommand *>(msg->removeControlInfo());
-            id = ind->getAssocId();
-            ServerAssocStatMap::iterator j=serverAssocStatMap.find(id);
-            BytesPerAssoc::iterator k=bytesPerAssoc.find(id);
-            if (j->second.rcvdBytes == 0)
-                j->second.start = simulation.getSimTime();
-
-            j->second.rcvdBytes+= PK(msg)->getByteLength();
-            k->second->record(j->second.rcvdBytes);
-
-            if (echoFactor==0)
-            {
-                if ((uint32)par("numPacketsToReceivePerClient")>0)
-                {
-                    j->second.rcvdPackets--;
-                    SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg);
-                    EndToEndDelay::iterator m=endToEndDelay.find(id);
-                    m->second->record(simulation.getSimTime()-smsg->getCreationTime());
-                    sctpEV3<<"server: Data received. Left packets to receive="<<j->second.rcvdPackets<<"\n";
-
-                    if (j->second.rcvdPackets == 0)
-                    {
-                        if (serverAssocStatMap[assocId].peerClosed==true && serverAssocStatMap[assocId].abortSent==false)
-                        {
-                            sendOrSchedule(makeAbortNotification(ind));
-                            serverAssocStatMap[assocId].abortSent = true;
-                            j->second.stop = simulation.getSimTime();
-                            j->second.lifeTime = j->second.stop - j->second.start;
-                            break;
-                        }
-                        else
-                        {
-                            cPacket* cmsg = new cPacket("Request");
-                            SCTPInfo* qinfo = new SCTPInfo("Info2");
-                            cmsg->setKind(SCTP_C_NO_OUTSTANDING);
-                            qinfo->setAssocId(id);
-                            cmsg->setControlInfo(qinfo);
-                            sendOrSchedule(cmsg);
-                            j->second.stop = simulation.getSimTime();
-                            j->second.lifeTime = j->second.stop - j->second.start;
-                        }
-                    }
-                }
-                delete msg;
-            }
-            else
+            else if (delayFirstRead==0 && readInt==false)
             {
-                SCTPSendCommand *cmd = new SCTPSendCommand("Send6");
-                cmd->setAssocId(id);
-                SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
-                EndToEndDelay::iterator n=endToEndDelay.find(id);
-                n->second->record(simulation.getSimTime()-smsg->getCreationTime());
-                cPacket* cmsg = new cPacket("SVData");
-                bytesSent+=smsg->getBitLength()/8;
-                cmd->setSendUnordered(cmd->getSendUnordered());
-                lastStream=(lastStream+1)%outboundStreams;
-                cmd->setSid(lastStream);
-                cmd->setLast(true);
-                cmsg->encapsulate(smsg);
-                cmsg->setKind(SCTP_C_SEND);
-                cmsg->setControlInfo(cmd);
-                packetsSent++;
-                delete msg;
-                sendOrSchedule(cmsg);
+               cmsg=makeReceiveRequest(PK(msg));
+               sendOrSchedule(cmsg);
             }
-            delete ind;
-            break;
-        }
-        case SCTP_I_SHUTDOWN_RECEIVED:
-        {
-            SCTPCommand *command = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
-            id = command->getAssocId();
-            sctpEV3<<"server: SCTP_I_SHUTDOWN_RECEIVED for assoc "<<id<<"\n";
-            ServerAssocStatMap::iterator i=serverAssocStatMap.find(id);
-            if (i->second.sentPackets == 0 || (long) par("numPacketsToSendPerClient")==0)
+
+         }
+         else
+         {
+            sctpEV3<<simulation.getSimTime()<<" makeReceiveRequest\n";
+            cmsg=makeReceiveRequest(PK(msg));
+            sendOrSchedule(cmsg);
+         }
+         delete msg;
+         break;
+      }
+      case SCTP_I_DATA:
+      {
+         notifications--;
+         packetsRcvd++;
+         sctpEV3<<simulation.getSimTime()<<" server: data arrived. "<<packetsRcvd<<" Packets received now\n";
+         SCTPRcvCommand *ind = check_and_cast<SCTPRcvCommand *>(msg->removeControlInfo());
+         id = ind->getAssocId();
+         ServerAssocStatMap::iterator j=serverAssocStatMap.find(id);
+         BytesPerAssoc::iterator k=bytesPerAssoc.find(id);
+         if (j->second.rcvdBytes == 0)
+            j->second.start = simulation.getSimTime();
+
+         j->second.rcvdBytes+= PK(msg)->getByteLength();
+         k->second->record(j->second.rcvdBytes);
+
+         if (echoFactor==0)
+         {
+            if ((long uint32)par("numPacketsToReceivePerClient")>0)
             {
-                cPacket* cmsg = new cPacket("Request");
-                SCTPInfo* qinfo = new SCTPInfo("Info3");
-                cmsg->setKind(SCTP_C_NO_OUTSTANDING);
-                qinfo->setAssocId(id);
-                cmsg->setControlInfo(qinfo);
-                sendOrSchedule(cmsg);
-                i->second.stop = simulation.getSimTime();
-                i->second.lifeTime = i->second.stop - i->second.start;
+               j->second.rcvdPackets--;
+               SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg);
+               EndToEndDelay::iterator m=endToEndDelay.find(id);
+               m->second->record(simulation.getSimTime()-smsg->getCreationTime());
+               sctpEV3<<"server: Data received. Left packets to receive="<<j->second.rcvdPackets<<"\n";
+
+               if (j->second.rcvdPackets == 0)
+               {
+                  if (serverAssocStatMap[assocId].peerClosed==true && serverAssocStatMap[assocId].abortSent==false)
+                  {
+                     sendOrSchedule(makeAbortNotification(ind));
+                     serverAssocStatMap[assocId].abortSent = true;
+                     j->second.stop = simulation.getSimTime();
+                     j->second.lifeTime = j->second.stop - j->second.start;
+                     break;
+                  }
+                  else
+                  {
+                     cPacket* cmsg = new cPacket("Request");
+                     SCTPInfo* qinfo = new SCTPInfo("Info2");
+                     cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+                     qinfo->setAssocId(id);
+                     cmsg->setControlInfo(qinfo);
+                     sendOrSchedule(cmsg);
+                     j->second.stop = simulation.getSimTime();
+                     j->second.lifeTime = j->second.stop - j->second.start;
+                  }
+               }
             }
-            delete command;
-            shutdownReceived = true;
             delete msg;
-            break;
-        }
-        case SCTP_I_CLOSED:
-            if (delayTimer->isScheduled())
-                cancelEvent(delayTimer);
-            if (finishEndsSimulation) {
-                endSimulation();
-            }
+         }
+         else
+         {
+            SCTPSendCommand *cmd = new SCTPSendCommand("Send6");
+            cmd->setAssocId(id);
+            SCTPSimpleMessage *smsg=check_and_cast<SCTPSimpleMessage*>(msg->dup());
+            EndToEndDelay::iterator n=endToEndDelay.find(id);
+            n->second->record(simulation.getSimTime()-smsg->getCreationTime());
+            cPacket* cmsg = new cPacket("SVData");
+            bytesSent+=smsg->getBitLength()/8;
+            cmd->setSendUnordered(cmd->getSendUnordered());
+            lastStream=(lastStream+1)%outboundStreams;
+            cmd->setPrValue(0);
+            cmd->setSid(lastStream);
+            cmd->setLast(true);
+            cmsg->encapsulate(smsg);
+            cmsg->setKind(SCTP_C_SEND);
+            cmsg->setControlInfo(cmd);
+            packetsSent++;
             delete msg;
-        break;
-        default: delete msg;
-    }
-    }
+            sendOrSchedule(cmsg);
+         }
+         delete ind;
+         break;
+      }
+      case SCTP_I_SHUTDOWN_RECEIVED:
+      {
+         SCTPCommand *command = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         id = command->getAssocId();
+         sctpEV3<<"server: SCTP_I_SHUTDOWN_RECEIVED for assoc "<<id<<"\n";
+         ServerAssocStatMap::iterator i=serverAssocStatMap.find(id);
+         if (i->second.sentPackets == 0 || (long) par("numPacketsToSendPerClient")==0)
+         {
+            cPacket* cmsg = new cPacket("Request");
+            SCTPInfo* qinfo = new SCTPInfo("Info3");
+            cmsg->setKind(SCTP_C_NO_OUTSTANDING);
+            qinfo->setAssocId(id);
+            cmsg->setControlInfo(qinfo);
+            sendOrSchedule(cmsg);
+            i->second.stop = simulation.getSimTime();
+            i->second.lifeTime = i->second.stop - i->second.start;
+         }
+         delete command;
+         shutdownReceived = true;
+         delete msg;
+         break;
+      }
+      case SCTP_I_SEND_STREAMS_RESETTED:
+      case SCTP_I_RCV_STREAMS_RESETTED:
+      {
+         ev<< "Streams have been resetted\n";
+         delete msg;
+         break;
+      }
+      case SCTP_I_CLOSED:
+         if (delayTimer->isScheduled())
+            cancelEvent(delayTimer);
+         if (finishEndsSimulation) {
+            endSimulation();
+         }
+         delete msg;
+      break;
+      default: delete msg;
+   }
+   }
 }
 
 void SCTPServer::handleTimer(cMessage *msg)
 {
-    cPacket* cmsg;
-    SCTPCommand* cmd;
-    int32 id;
-    double tempInterval;
-
-    if (msg==delayTimer)
-    {
-        ServerAssocStatMap::iterator i=serverAssocStatMap.find(assocId);
-        sctpEV3<<simulation.getSimTime()<<" delayTimer expired\n";
-        sendOrSchedule(makeDefaultReceive());
-        scheduleAt(simulation.getSimTime()+(double)par("readingInterval"), delayTimer);
-        return;
-    }
-    else if (msg==delayFirstReadTimer)
-    {
-        delayFirstRead = 0;
-
-        if (readInt && !delayTimer->isScheduled())
-        {
-            tempInterval = (double)par("readingInterval");
-            scheduleAt(simulation.getSimTime()+(simtime_t)tempInterval, delayTimer);
-            scheduleAt(simulation.getSimTime()+(simtime_t)tempInterval, makeDefaultReceive());
-        }
-        return;
-    }
-
-    switch (msg->getKind())
-    {
-    case SCTP_C_SEND:
-        if (numRequestsToSend>0)
-        {
-            generateAndSend();
-            if ((simtime_t)par("thinkTime") > 0)
-                scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
-            numRequestsToSend--;
-        }
-        break;
-    case SCTP_I_ABORT:
-
-        cmsg = new cPacket("CLOSE", SCTP_C_CLOSE);
-        cmd = new SCTPCommand("Send6");
-        id = atoi(msg->getName());
-              cmd->setAssocId(id);
-        cmsg->setControlInfo(cmd);
-        sendOrSchedule(cmsg);
-        break;
-    case SCTP_C_RECEIVE:
-        sctpEV3<<simulation.getSimTime()<<" SCTPServer:SCTP_C_RECEIVE\n";
-        if (readInt || delayFirstRead > 0)
-            schedule = false;
-        else
-            schedule = true;
-        sendOrSchedule(PK(msg));
-        break;
-    default:
-
-        sctpEV3<<"MsgKind ="<<msg->getKind()<<" unknown\n";
-
-        break;
-    }
+   cPacket* cmsg;
+   SCTPCommand* cmd;
+   int32 id;
+   double tempInterval;
+
+   if (msg==delayTimer)
+   {
+      // ServerAssocStatMap::iterator i=serverAssocStatMap.find(assocId);
+      sctpEV3<<simulation.getSimTime()<<" delayTimer expired\n";
+      sendOrSchedule(makeDefaultReceive());
+      scheduleAt(simulation.getSimTime()+(double)par("readingInterval"), delayTimer);
+      return;
+   }
+   else if (msg==delayFirstReadTimer)
+   {
+      delayFirstRead = 0;
+
+      if (readInt && !delayTimer->isScheduled())
+      {
+         tempInterval = (double)par("readingInterval");
+         scheduleAt(simulation.getSimTime()+(simtime_t)tempInterval, delayTimer);
+         scheduleAt(simulation.getSimTime()+(simtime_t)tempInterval, makeDefaultReceive());
+      }
+      return;
+   }
+
+   switch (msg->getKind())
+   {
+   case SCTP_C_SEND:
+      if (numRequestsToSend>0)
+      {
+         generateAndSend();
+         if ((simtime_t)par("thinkTime") > 0)
+            scheduleAt(simulation.getSimTime()+(simtime_t)par("thinkTime"), timeoutMsg);
+         numRequestsToSend--;
+      }
+      break;
+   case SCTP_I_ABORT:
+
+      cmsg = new cPacket("CLOSE", SCTP_C_CLOSE);
+      cmd = new SCTPCommand("Send6");
+      id = atoi(msg->getName());
+           cmd->setAssocId(id);
+      cmsg->setControlInfo(cmd);
+      sendOrSchedule(cmsg);
+      break;
+   case SCTP_C_RECEIVE:
+      sctpEV3<<simulation.getSimTime()<<" SCTPServer:SCTP_C_RECEIVE\n";
+      if (readInt || delayFirstRead > 0)
+         schedule = false;
+      else
+         schedule = true;
+      sendOrSchedule(PK(msg));
+      break;
+   default:
+
+      sctpEV3<<"MsgKind ="<<msg->getKind()<<" unknown\n";
+
+      break;
+   }
 }
 
 void SCTPServer::finish()
 {
-    delete timeoutMsg;
-    if (delayTimer->isScheduled())
-        cancelEvent(delayTimer);
-    delete delayTimer;
-    delete delayFirstReadTimer;
-
-        ev << getFullPath() << ": opened " << numSessions << " sessions\n";
-    ev << getFullPath() << ": sent " << bytesSent << " bytes in " << packetsSent << " packets\n";
-    for (ServerAssocStatMap::iterator l=serverAssocStatMap.begin(); l!=serverAssocStatMap.end(); l++)
-    {
-        ev << getFullPath() << " Assoc: "<<l->first<<"\n";
-        ev << "\tstart time: "<<l->second.start <<"\n";
-        ev << "\tstop time: "<<l->second.stop <<"\n";
-        ev << "\tlife time: "<<l->second.lifeTime <<"\n";
-        ev << "\treceived bytes:" << l->second.rcvdBytes << "\n";
-        ev << "\tthroughput: "<<(l->second.rcvdBytes / l->second.lifeTime.dbl())*8 <<" bit/sec\n";
-        recordScalar("bytes rcvd", l->second.rcvdBytes);
-        recordScalar("throughput", (l->second.rcvdBytes / l->second.lifeTime.dbl())*8);
-
-    }
-    ev << getFullPath() << "Over all " << packetsRcvd << " packets received\n ";
-    ev << getFullPath() << "Over all " << notifications << " notifications received\n ";
-
-    BytesPerAssoc::iterator j;
-    while ((j = bytesPerAssoc.begin())!= bytesPerAssoc.end())
-    {
-        delete j->second;
-        bytesPerAssoc.erase(j);
-    }
-    EndToEndDelay::iterator k;
-    while ((k = endToEndDelay.begin())!= endToEndDelay.end())
-    {
-        delete k->second;
-        endToEndDelay.erase(k);
-    }
-    serverAssocStatMap.clear();
-    sctpEV3<<"Server finished\n";
+   delete timeoutMsg;
+   if (delayTimer->isScheduled())
+      cancelEvent(delayTimer);
+   delete delayTimer;
+   delete delayFirstReadTimer;
+
+      ev << getFullPath() << ": opened " << numSessions << " sessions\n";
+   ev << getFullPath() << ": sent " << bytesSent << " bytes in " << packetsSent << " packets\n";
+   for (ServerAssocStatMap::iterator l=serverAssocStatMap.begin(); l!=serverAssocStatMap.end(); l++)
+   {
+      ev << getFullPath() << " Assoc: "<<l->first<<"\n";
+      ev << "\tstart time: "<<l->second.start <<"\n";
+      ev << "\tstop time: "<<l->second.stop <<"\n";
+      ev << "\tlife time: "<<l->second.lifeTime <<"\n";
+      ev << "\treceived bytes:" << l->second.rcvdBytes << "\n";
+      ev << "\tthroughput: "<<(l->second.rcvdBytes / l->second.lifeTime.dbl())*8 <<" bit/sec\n";
+      recordScalar("bytes rcvd", l->second.rcvdBytes);
+      recordScalar("throughput", (l->second.rcvdBytes / l->second.lifeTime.dbl())*8);
+
+   }
+   ev << getFullPath() << "Over all " << packetsRcvd << " packets received\n ";
+   ev << getFullPath() << "Over all " << notifications << " notifications received\n ";
+
+   BytesPerAssoc::iterator j;
+   while ((j = bytesPerAssoc.begin())!= bytesPerAssoc.end())
+   {
+      delete j->second;
+      bytesPerAssoc.erase(j);
+   }
+   EndToEndDelay::iterator k;
+   while ((k = endToEndDelay.begin())!= endToEndDelay.end())
+   {
+      delete k->second;
+      endToEndDelay.erase(k);
+   }
+   serverAssocStatMap.clear();
+   sctpEV3<<"Server finished\n";
 }
 
 SCTPServer::~SCTPServer()
 {
-    delete socket;
+   delete socket;
 }
diff --git a/src/applications/sctpapp/SCTPServer.h b/src/applications/sctpapp/SCTPServer.h
index 1ecae28..2bbe4b2 100644
--- a/src/applications/sctpapp/SCTPServer.h
+++ b/src/applications/sctpapp/SCTPServer.h
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2009 Thomas Dreibholz
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -26,71 +26,71 @@
 
 class INET_API SCTPServer : public cSimpleModule
 {
-    protected:
-        int32 notifications;
-        int32 assocId;
-        SCTPSocket *socket;
-        double delay;
-        double echoFactor;
-        double delayFirstRead;
-        bool readInt;
-        bool schedule;
-        bool firstData;
-        bool shutdownReceived;
-        uint64 bytesSent;
-        uint64 packetsSent;
-        uint64 packetsRcvd;
-        int32 numSessions;
-        uint64 numRequestsToSend; // requests to send in this session
-        bool finishEndsSimulation;
-        bool ordered;
-        bool abortSent;
-        int32 queueSize;
-        int32 count;
-        cMessage *timeoutMsg;
-        cMessage *delayTimer;
-        cMessage *delayFirstReadTimer;
-        //cPacket* abort;
-        int32 inboundStreams;
-        int32 outboundStreams;
-        int32 lastStream;
-        typedef struct
-        {
-            simtime_t start;
-            simtime_t stop;
-            uint64 rcvdBytes;
-            uint64 sentPackets;
-            uint64 rcvdPackets;
-            simtime_t lifeTime;
-            bool abortSent;
-            bool peerClosed;
-        }ServerAssocStat;
-        typedef std::map<int32,ServerAssocStat> ServerAssocStatMap;
-        ServerAssocStatMap serverAssocStatMap;
-        typedef std::map<int32,cOutVector*> BytesPerAssoc;
-        BytesPerAssoc bytesPerAssoc;
-        typedef std::map<int32,cDoubleHistogram*> HistEndToEndDelay;
-        HistEndToEndDelay histEndToEndDelay;
-        typedef std::map<int32,cOutVector*> EndToEndDelay;
-        EndToEndDelay endToEndDelay;
-        void sendOrSchedule(cPacket *msg);
-        cPacket* makeAbortNotification(SCTPCommand* msg);
-        cPacket* makeReceiveRequest(cPacket* msg);
-        cPacket* makeDefaultReceive();
-        int32 ssn;
-    public:
-        ~SCTPServer();
-        struct pathStatus {
-            bool active;
-            bool primaryPath;
-            IPAddress  pid;
-        };
+   protected:
+      int32 notifications;
+      int32 assocId;
+      SCTPSocket *socket;
+      double delay;
+      double echoFactor;
+      double delayFirstRead;
+      bool readInt;
+      bool schedule;
+      bool firstData;
+      bool shutdownReceived;
+      uint64 bytesSent;
+      uint64 packetsSent;
+      uint64 packetsRcvd;
+      int32 numSessions;
+      uint64 numRequestsToSend; // requests to send in this session
+      bool finishEndsSimulation;
+      bool ordered;
+      bool abortSent;
+      int32 queueSize;
+      int32 count;
+      cMessage *timeoutMsg;
+      cMessage *delayTimer;
+      cMessage *delayFirstReadTimer;
+      //cPacket* abort;
+      int32 inboundStreams;
+      int32 outboundStreams;
+      int32 lastStream;
+      typedef struct
+      {
+         simtime_t start;
+         simtime_t stop;
+         uint64 rcvdBytes;
+         uint64 sentPackets;
+         uint64 rcvdPackets;
+         simtime_t lifeTime;
+         bool abortSent;
+         bool peerClosed;
+      }ServerAssocStat;
+      typedef std::map<int32,ServerAssocStat> ServerAssocStatMap;
+      ServerAssocStatMap serverAssocStatMap;
+      typedef std::map<int32,cOutVector*> BytesPerAssoc;
+      BytesPerAssoc bytesPerAssoc;
+      typedef std::map<int32,cDoubleHistogram*> HistEndToEndDelay;
+      HistEndToEndDelay histEndToEndDelay;
+      typedef std::map<int32,cOutVector*> EndToEndDelay;
+      EndToEndDelay endToEndDelay;
+      void sendOrSchedule(cPacket *msg);
+      cPacket* makeAbortNotification(SCTPCommand* msg);
+      cPacket* makeReceiveRequest(cPacket* msg);
+      cPacket* makeDefaultReceive();
+      int32 ssn;
+   public:
+      ~SCTPServer();
+      struct pathStatus {
+         bool active;
+         bool primaryPath;
+         IPAddress  pid;
+      };
 
-        void initialize();
-        void handleMessage(cMessage *msg);
-        void finish();
-        void handleTimer(cMessage *msg);
-        void generateAndSend();
+      void initialize();
+      void handleMessage(cMessage *msg);
+      void finish();
+      void handleTimer(cMessage *msg);
+      void generateAndSend();
 };
 
 #endif
diff --git a/src/applications/sctpapp/SCTPServer.ned b/src/applications/sctpapp/SCTPServer.ned
index afc1bc1..3b2be29 100644
--- a/src/applications/sctpapp/SCTPServer.ned
+++ b/src/applications/sctpapp/SCTPServer.ned
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2009 Thomas Dreibholz
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -21,26 +21,29 @@ package inet.applications.sctpapp;
 
 simple SCTPServer like SCTPApp
 {
-    parameters:
-         string address = default(""); // may be left empty ("")
-        int port = default(0); // port number to listen on
-        int echoFactor = default(0);
-        volatile double echoDelay @unit(s) = default(0s);
-        double delayFirstRead @unit(s) = default(0s);
-        volatile double readingInterval @unit(s) = default(0s);
-        int messagesToPush = default(0);
-        int numPacketsToSendPerClient = default(0); // number of requests sent per session
-        int numPacketsToReceivePerClient = default(1);
-        int requestLength = default(1452); // length of a request (bytes)
-        volatile double thinkTime @unit(s) = default(0s); // time gap between requests
-        double waitToClose @unit(s) = default(0s); //time to wait between last message sent and abort
-        bool finishEndsSimulation = default(false);
-        bool ordered = default(true);
-        int inboundStreams = default(17);
-        int outboundStreams = default(1);
-        int queueSize = default(0);
-    gates:
-        input sctpIn;
-        output sctpOut;
+   parameters:
+       string address = default(""); // may be left empty ("")
+      int port = default(0); // port number to listen on
+      int echoFactor = default(0);
+      volatile double echoDelay @unit(s) = default(0s);
+      double delayFirstRead @unit(s) = default(0s);
+      volatile double readingInterval @unit(s) = default(0s);
+      int messagesToPush = default(0);
+      int numPacketsToSendPerClient = default(0);  // number of requests sent per session
+      int numPacketsToReceivePerClient = default(1);
+      int requestLength = default(1452); // length of a request (bytes)
+      volatile double thinkTime @unit(s) = default(0s); // time gap between requests
+      double waitToClose @unit(s) = default(0s); //time to wait between last message sent and abort
+      bool finishEndsSimulation = default(false);
+      bool ordered = default(true);
+      int inboundStreams = default(17);
+      int outboundStreams = default(1);
+      int queueSize = default(0);
+      int prMethod = default(0);  //0=NONE, 1=PR_TTL, 2=PR_RTX, 3=PR_PRIO, 4=PR_STRRST
+      bool streamReset = default(false);
+      double prValue = default(0); //for PR-SCTP
+      string streamPriorities = default("");
+   gates:
+      input sctpIn;
+      output sctpOut;
 }
-
diff --git a/src/linklayer/ppp/PPP.cc b/src/linklayer/ppp/PPP.cc
index 01e833a..736ee88 100644
--- a/src/linklayer/ppp/PPP.cc
+++ b/src/linklayer/ppp/PPP.cc
@@ -1,5 +1,7 @@
 //
 // Copyright (C) 2004 Andras Varga
+// Copyright (C) 2007 Irene Ruengeler
+// Copyright (C) 2010-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public License
@@ -220,19 +222,23 @@ void PPP::handleMessage(cMessage *msg)
         }
 
         // check for bit errors
+        // T.D./I.R.: Deliver damaged message to upper layer, but mark it as damaged.
+        // The damaged payload is needed by SCTP PKTDROP!
+        cPacket *payload;
         if (PK(msg)->hasBitError())
         {
             EV << "Bit error in " << msg << endl;
             numBitErr++;
-            delete msg;
+            payload = decapsulate(check_and_cast<PPPFrame*>(msg));
+            PK(payload)->setBitError(true);
         }
         else
         {
             // pass up payload
-            cPacket *payload = decapsulate(check_and_cast<PPPFrame *>(msg));
+            payload = decapsulate(check_and_cast<PPPFrame *>(msg));
             numRcvdOK++;
-            send(payload,"netwOut");
         }
+        send(payload,"netwOut");
     }
     else // arrived on gate "netwIn"
     {
diff --git a/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.cc b/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.cc
new file mode 100644
index 0000000..ef30061
--- /dev/null
+++ b/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.cc
@@ -0,0 +1,435 @@
+// * --------------------------------------------------------------------------
+// *
+// *     //====//  //===== <===//===>  //====//
+// *    //        //          //      //    //    SCTP Optimization Project
+// *   //=====   //          //      //====//   ==============================
+// *        //  //          //      //           University of Duisburg-Essen
+// *  =====//  //=====     //      //
+// *
+// * --------------------------------------------------------------------------
+// *
+// *   Copyright (C) 2009-2012 by Thomas Dreibholz
+// *
+// *   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 3 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+// *
+// *   Contact: dreibh@iem.uni-due.de
+
+#include <algorithm>
+#include "IRoutingTable.h"
+#include "IInterfaceTable.h"
+#include "IPAddressResolver.h"
+#include "MultihomedFlatNetworkConfigurator.h"
+#include "InterfaceEntry.h"
+#include "IPv4InterfaceData.h"
+
+
+Define_Module(MultihomedFlatNetworkConfigurator);
+
+
+// ###### Initialize: perform autorouting ###################################
+void MultihomedFlatNetworkConfigurator::initialize(int stage)
+{
+   if(stage == 2) {
+      cTopology      fullTopology;
+      NodeInfoVector fullNodeInfoVector;
+      extractTopology(fullTopology, fullNodeInfoVector, true, true, 0);
+
+      // Assign addresses to all nodes
+      std::set<unsigned int> networkSet = assignAddresses(fullTopology, fullNodeInfoVector);
+
+      // Compute routing tables for each network ID
+      for(std::set<unsigned int>::iterator iterator = networkSet.begin();
+         iterator != networkSet.end(); iterator++) {
+         const unsigned int networkID = *iterator;
+         EV << "###### Computing routing table for network ID " << networkID << " ... ######" << endl;
+
+         cTopology      fullTopologyForNetwork;
+         NodeInfoVector fullNodeInfoVectorForNetwork;
+         extractTopology(fullTopologyForNetwork, fullNodeInfoVectorForNetwork, true, false, networkID);
+
+         computeRouting(fullTopologyForNetwork, fullNodeInfoVectorForNetwork, networkID);      
+         // dumpConfiguration(fullTopologyForNetwork, fullNodeInfoVectorForNetwork, true);
+      }
+      
+      dumpConfiguration(fullTopology, fullNodeInfoVector);
+      setDisplayString(fullTopology, fullNodeInfoVector);
+   }
+}
+
+
+// ###### Update route if new one has a better metric #######################
+static void updateIfMetricIsBetter(IRoutingTable* routingTable,
+                                   IPRoute*       newRoute)
+{
+   const IPRoute* oldRoute = routingTable->findRoute(newRoute->getHost(), newRoute->getNetmask(), IPAddress());
+   if(oldRoute) {
+      if(oldRoute->getMetric() <= newRoute->getMetric()) {
+         // Old route is better or equal -> keep it and ignore new one.
+         delete newRoute;
+         return;
+      }
+      else {
+         // New route is better -> get rid of the old one.
+         routingTable->deleteRoute(oldRoute);
+      }
+   }
+      
+   std::vector<IPAddress> ownAddresses = routingTable->gatherAddresses();
+   for(std::vector<IPAddress>::iterator iterator = ownAddresses.begin();
+       iterator != ownAddresses.end(); iterator++) {
+      if(newRoute->getHost() == *iterator) {
+          // New route is a loop to myself -> ignore it.
+         delete newRoute;
+         return;
+      }
+   }
+   
+   routingTable->addRoute(newRoute);
+}
+
+
+// ###### Get Network ID from gate ##########################################
+static unsigned int getNetworkID(cChannel* channel)
+{
+   unsigned int networkID = 0;
+   if(channel) {
+      if(channel->hasPar("netID")) {
+         networkID = channel->par("netID");
+      }
+   }
+   return(networkID);
+}
+
+// ###### Get Network ID from gate ##########################################
+static unsigned int getNetworkID(cModule*        module,
+                                 InterfaceEntry* interfaceEntry)
+{
+   int          outputGateID = interfaceEntry->getNodeOutputGateId();
+   cGate*       outputGate   = module->gate(outputGateID);
+   cChannel*    channel      = outputGate->getChannel();
+   return(getNetworkID(channel));
+}
+
+
+// ###### Get Network ID from gate ##########################################
+static unsigned int getNetworkID(cModule*            module,
+                                 cTopology::LinkOut* link)
+{
+   int          outputGateID = link->getLocalGateId();
+   cGate*       outputGate   = module->gate(outputGateID);
+   cChannel*    channel      = outputGate->getChannel();
+   return(getNetworkID(channel));
+}
+
+
+
+// ###### Search filter to find nodes or routers only #######################
+struct NodeFilterParameters
+{
+   bool         AllNetworks;
+   bool         FullTopology;
+   unsigned int NetworkID;
+};
+
+static bool nodeFilter(cModule* module, void* userData)
+{
+   const NodeFilterParameters* parameters = (const NodeFilterParameters*)userData;
+
+   cProperty* nodeProperty = module->getProperties()->get("node");
+   if(nodeProperty) {
+      // ====== Check whether this is a router ==============================
+      IRoutingTable* routingTable = IPAddressResolver().routingTableOf(module);
+      if(routingTable) {
+         if( (parameters->FullTopology) || (routingTable->isIPForwardingEnabled()) ) {
+            // ====== Are nodes in arbitrary networks requested? ============
+            if(parameters->AllNetworks) {
+               return(true);
+            }
+
+            // ====== Is there an interface in the right network? ===========
+            IInterfaceTable* interfaceTable = IPAddressResolver().interfaceTableOf(module);
+            if(interfaceTable) {
+               bool foundNetwork = false;
+               for(int k = 0;k < interfaceTable->getNumInterfaces(); k++) {
+                  InterfaceEntry*    interfaceEntry     = interfaceTable->getInterface(k);
+                  if(!interfaceEntry->isLoopback()) {
+                     const unsigned int interfaceNetworkID = getNetworkID(module, interfaceEntry);
+                     if( (interfaceNetworkID == 0) ||
+                         (interfaceNetworkID == parameters->NetworkID) ) {
+                        foundNetwork = true;
+                        break;
+                     }
+                  }
+               }
+               if(foundNetwork) {
+                  return(true);
+               }
+            }
+         }
+      }
+   }
+   return(false);
+}
+
+
+// ###### Extract the topology (full or routers only) #######################
+void MultihomedFlatNetworkConfigurator::extractTopology(cTopology&      topology,
+                                                        NodeInfoVector& nodeInfo,
+                                                        const bool      fullTopology,
+                                                        const bool      allNetworks,
+                                                        unsigned int    networkID)
+{
+   NodeFilterParameters parameters;
+   parameters.FullTopology = fullTopology;
+   parameters.AllNetworks  = allNetworks;
+   parameters.NetworkID    = networkID;
+
+   topology.extractFromNetwork(nodeFilter, &parameters);
+   EV << "cTopology found " << topology.getNumNodes() << " nodes for network "
+      << networkID << endl;
+
+   nodeInfo.resize(topology.getNumNodes());
+   for(int i = 0; i < topology.getNumNodes(); i++) {
+      // ====== Cache pointers to interface and routing tables ==============
+      cTopology::Node* node   = topology.getNode(i);
+      cModule*         module = node->getModule();
+      nodeInfo[i].isIPNode = (IPAddressResolver().findInterfaceTableOf(module) != NULL);
+      if(nodeInfo[i].isIPNode) {
+         nodeInfo[i].interfaceTable = IPAddressResolver().interfaceTableOf(module);
+         nodeInfo[i].routingTable   = IPAddressResolver().routingTableOf(module);
+      }
+
+      // ====== Prune links having the wrong network ID =====================
+      for(int j = 0; j < node->getNumOutLinks(); j++) {
+         cTopology::LinkOut* link          = node->getLinkOut(j);
+         const unsigned int  linkNetworkID = getNetworkID(module, link);
+         if( (linkNetworkID != networkID) && (linkNetworkID != 0) ) {
+            link->disable();
+         }
+      }
+   }
+}
+
+
+#define MAX_HOSTS_SHIFT    16
+#define MAX_NETWORKS_SHIFT (32 - MAX_HOSTS_SHIFT - 2)
+
+// ###### Assign addresses to all nodes #####################################
+std::set<unsigned int> MultihomedFlatNetworkConfigurator::assignAddresses(cTopology&      topology,
+                                                                          NodeInfoVector& nodeInfo)
+{
+   // ====== Initialize per-network node counters ===========================
+   unsigned int hostsOfNetwork[1 << MAX_NETWORKS_SHIFT];
+   for(unsigned int i = 0;i < (1 << MAX_NETWORKS_SHIFT);i++) {
+      hostsOfNetwork[i] = 0;
+   }
+   std::set<unsigned int> networkSet;
+
+   TotalNumberOfInterfaces = 0;
+   for(int i=0; i<topology.getNumNodes(); i++) {
+      // ====== Skip bus types ==============================================
+      if (!nodeInfo[i].isIPNode) {
+          continue;
+      }
+
+      // ====== Assign address to each IP interface =========================
+      IInterfaceTable* interfaceTable = nodeInfo[i].interfaceTable;
+      cTopology::Node* atNode         = topology.getNode(i);
+      EV << "Node " << atNode->getModule()->getFullName() << ":" << endl;
+      for(int k = 0; k < interfaceTable->getNumInterfaces(); k++) {
+         InterfaceEntry* interfaceEntry = interfaceTable->getInterface(k);
+         if(!interfaceEntry->isLoopback()) {
+            const unsigned int networkID = getNetworkID(atNode->getModule(), interfaceEntry);
+            networkSet.insert(networkID);
+
+            if(networkID >= (1 << MAX_NETWORKS_SHIFT) - 1) {
+               error("Network ID %u is too large!", networkID);
+            }
+
+            const uint32 address =
+               0x80000000                     |   // beginning with binary 10 (i.e. 128.0.0.0 .. 191.255.255.255)
+               (networkID << MAX_HOSTS_SHIFT) |
+               (++hostsOfNetwork[networkID]);
+            TotalNumberOfInterfaces++;
+
+            if(hostsOfNetwork[networkID] >= (1 << (32 - 2 - MAX_NETWORKS_SHIFT)) - 1) {
+               error("Too many hosts (%u) for network ID %u!", hostsOfNetwork[networkID], networkID);
+            }
+
+            EV << "   Interface " << interfaceEntry->getName() << ": "
+               << IPAddress(address).str().c_str() << endl;
+            interfaceEntry->ipv4Data()->setIPAddress(IPAddress(address));
+            interfaceEntry->ipv4Data()->setNetmask(IPAddress::ALLONES_ADDRESS);
+         }
+      }
+   }
+   return(networkSet);
+}
+
+
+// ###### Print configuration computed ######################################
+void MultihomedFlatNetworkConfigurator::dumpConfiguration(cTopology&      topology,
+                                                          NodeInfoVector& nodeInfo,
+                                                          const bool      intermediateVersion)
+{
+   EV << "Routing Configuration by MultihomedFlatNetworkConfigurator: --------------" << endl;
+   for(int n = 0; n < topology.getNumNodes(); n++) {
+      cTopology::Node* node               = topology.getNode(n);
+      cModule*         nodeModule         = node->getModule();
+      IRoutingTable*   nodeRoutingTable   = nodeInfo[n].routingTable;
+      IInterfaceTable* nodeInterfaceTable = nodeInfo[n].interfaceTable;
+      
+      EV << "Node " << nodeModule->getFullPath() << ":" << endl;
+      EV << "   Forwarding: " << (nodeRoutingTable->isIPForwardingEnabled() ? "yes" : "no") << endl;
+
+/*
+      for(int k = 0;k < nodeInterfaceTable->getNumInterfaces();k++) {
+         InterfaceEntry* interfaceEntry = nodeInterfaceTable->getInterface(k);
+         EV << "   Interface " << interfaceEntry->getName() << ":\t"
+            << interfaceEntry->ipv4Data()->getIPAddress().str().c_str() << endl;
+      }
+*/
+
+      unsigned int num = 0;
+      for(int j = 0; j < node->getNumOutLinks(); j++) {
+         cTopology::Node* neighbourNode               = node->getLinkOut(j)->getRemoteNode();
+         IInterfaceTable* neighbourNodeInterfaceTable = IPAddressResolver().interfaceTableOf(neighbourNode->getModule());
+         const int        neighbourGateID             = node->getLinkOut(j)->getRemoteGate()->getId();
+         InterfaceEntry*  neighbourInterfaceEntry     = neighbourNodeInterfaceTable->getInterfaceByNodeInputGateId(neighbourGateID);
+         const int        outputGateID                = node->getLinkOut(j)->getLocalGate()->getId();
+         InterfaceEntry*  outputInterfaceEntry        = nodeInterfaceTable->getInterfaceByNodeOutputGateId(outputGateID);
+         unsigned int     outputInterfaceNetworkID    = getNetworkID(node->getModule(), outputInterfaceEntry);
+
+         EV << ++num << "   Interface "
+               << outputInterfaceEntry->getName() << ":\t"
+               << outputInterfaceEntry->ipv4Data()->getIPAddress().str().c_str()
+            << "\t<-->\t"
+               << neighbourNode->getModule()->getFullPath() << " Interface "
+               << neighbourInterfaceEntry->getName() << ":\t"
+               << neighbourInterfaceEntry->ipv4Data()->getIPAddress().str().c_str()
+            << "\tNetID: " << outputInterfaceNetworkID
+            << endl;
+      }
+
+      if(nodeInfo[n].usesDefaultRoute) {
+         EV << "   Using default route" << endl;
+      }
+      else {
+         for (int r = 0;r< nodeRoutingTable->getNumRoutes();r++) {
+            const IPRoute* route = nodeRoutingTable->getRoute(r);
+            ev << ++num << "   Route to "
+               << route->getHost().str().c_str() << " via "
+               << route->getInterface()->getName() << ", metric "
+               << route->getMetric() << endl;
+         }
+         if( ((unsigned int)nodeRoutingTable->getNumRoutes() > TotalNumberOfInterfaces) &&
+             (!intermediateVersion) ) {
+            error("Routing table has more routes (%u) than interfaces in the network (%u)!",
+                  (unsigned int)nodeRoutingTable->getNumRoutes(), TotalNumberOfInterfaces);
+         }
+      }
+   }
+   EV << "--------------------------------------------------------------------------" << endl;
+}
+
+
+// ###### Set MultihomedFlatNetworkConfigurator description text ############
+void MultihomedFlatNetworkConfigurator::setDisplayString(cTopology&      topology,
+                                                         NodeInfoVector& nodeInfo)
+{
+   int numIPNodes = 0;
+   for(int i = 0; i < topology.getNumNodes(); i++) {
+      if(nodeInfo[i].isIPNode) {
+         numIPNodes++;
+      }
+   }
+
+   char buffer[80];
+   snprintf(buffer, sizeof(buffer), "%d IP nodes\n%d non-IP nodes",
+            numIPNodes, topology.getNumNodes() - numIPNodes);
+   getDisplayString().setTagArg("t", 0, buffer);
+}
+
+
+// ###### Compute the routing tables for given network ID ###################
+void MultihomedFlatNetworkConfigurator::computeRouting(cTopology&         topology,
+                                                       NodeInfoVector&    nodeInfo,
+                                                       const unsigned int networkID)
+{
+   for(int i = 0;i < topology.getNumNodes();i++) {
+      // ====== Get destination router data =================================
+      cTopology::Node* destinationNode = topology.getNode(i);
+      // skip bus types
+      if(!nodeInfo[i].isIPNode) {
+         continue;
+      }
+      IInterfaceTable* destinationNodeInterfaceTable = nodeInfo[i].interfaceTable;
+
+      // ====== Calculate shortest paths ====================================
+      topology.calculateUnweightedSingleShortestPathsTo(destinationNode);
+
+      
+      // ====== Update routing tables of all nodes with shortest paths ======
+      for (int j = 0; j < topology.getNumNodes(); j++) {
+         // ====== Is this node useful? =====================================
+         cTopology::Node* atNode = topology.getNode(j);
+         if( (i == j) || (!nodeInfo[j].isIPNode) ) {
+            continue;   // same node or bus type
+         }
+         if(atNode->getNumPaths() == 0) {
+            continue;   // not connected
+         }
+
+         // ====== Get output interface at node "atNode" ====================
+         IInterfaceTable* sourceNodeInterfaceTable = nodeInfo[j].interfaceTable;
+         const int        outputGateId             = atNode->getPath(0)->getLocalGate()->getId();
+         InterfaceEntry*  outputInterfaceEntry     = sourceNodeInterfaceTable->getInterfaceByNodeOutputGateId(outputGateId);
+         if(outputInterfaceEntry == NULL) {
+            error("%s has no interface for output gate id %d",
+                  sourceNodeInterfaceTable->getFullPath().c_str(), outputGateId);
+         }
+         
+         // ====== Get input interface at node "destinationNode" ============
+         cTopology::LinkOut* path = atNode->getPath(0);
+         while(path->getRemoteNode() != destinationNode) {
+            cTopology::Node* nextRouter = path->getRemoteNode();
+            path = nextRouter->getPath(0);
+         }
+         
+         // ====== Add routing table entries for the destination router =====
+         IRoutingTable* routingTable = nodeInfo[j].routingTable;
+         for(int k = 0;k < destinationNodeInterfaceTable->getNumInterfaces();k++) {
+            // Add a routing table entry for each interface of the destination ...
+            InterfaceEntry* interfaceEntry = destinationNodeInterfaceTable->getInterface(k);
+            if(interfaceEntry->isLoopback()) {
+               continue;   // ... except for its loopback address
+            }
+            const unsigned int destinationNetworkID = getNetworkID(destinationNode->getModule(), interfaceEntry);
+            if( (destinationNetworkID != networkID) && (destinationNetworkID != 0) ) {
+               continue;   // ... except for links belonging to the wrong network
+            }
+            const IPAddress destinationAddress = interfaceEntry->ipv4Data()->getIPAddress();
+
+            IPRoute* route = new IPRoute();
+            route->setHost(destinationAddress);
+            route->setNetmask(IPAddress(255,255,255,255));      // full match needed
+            route->setInterface(outputInterfaceEntry);
+            route->setType(IPRoute::DIRECT);
+            route->setSource(IPRoute::MANUAL);
+            route->setMetric(atNode->getDistanceToTarget());    // hop count metric
+            updateIfMetricIsBetter(routingTable, route);
+         }
+      }
+   }
+}
diff --git a/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.h b/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.h
new file mode 100644
index 0000000..f6bae47
--- /dev/null
+++ b/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.h
@@ -0,0 +1,79 @@
+// * --------------------------------------------------------------------------
+// *
+// *     //====//  //===== <===//===>  //====//
+// *    //        //          //      //    //    SCTP Optimization Project
+// *   //=====   //          //      //====//   ==============================
+// *        //  //          //      //           University of Duisburg-Essen
+// *  =====//  //=====     //      //
+// *
+// * --------------------------------------------------------------------------
+// *
+// *   Copyright (C) 2009-2012 by Thomas Dreibholz
+// *
+// *   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 3 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+// *
+// *   Contact: dreibh@iem.uni-due.de
+
+#ifndef __INET_MULTIHOMEDFLATNETWORKCONFIGURATOR_H
+#define __INET_MULTIHOMEDFLATNETWORKCONFIGURATOR_H
+
+#include <omnetpp.h>
+#include <set>
+#include "FlatNetworkConfigurator.h"
+
+
+class INET_API MultihomedFlatNetworkConfigurator : public cSimpleModule
+{
+   protected:
+   struct NodeInfo {
+      NodeInfo() {
+         isIPNode         = false;
+         interfaceTable   = NULL;
+         routingTable     = NULL;
+         usesDefaultRoute = false; }
+      bool             isIPNode;
+      IInterfaceTable* interfaceTable;
+      IRoutingTable*   routingTable;
+      bool             usesDefaultRoute;
+   };
+   typedef std::vector<NodeInfo> NodeInfoVector;
+  
+
+   protected:
+   virtual int numInitStages() const { return(3); }
+   virtual void initialize(int stage);
+   virtual void extractTopology(cTopology&         topology,
+                                NodeInfoVector&    nodeInfo,
+                                const bool         fullTopology,
+                                const bool         allNetworks,
+                                const unsigned int networkID);
+   virtual std::set<unsigned int> assignAddresses(cTopology&      topology,
+                                                  NodeInfoVector& nodeInfo);
+   virtual void computeRouting(cTopology&         topology,
+                               NodeInfoVector&    nodeInfo,
+                               const unsigned int networkID);
+   virtual void setDisplayString(cTopology&      topology,
+                                 NodeInfoVector& nodeInfo);
+
+
+   private:
+   typedef std::set<IPAddress> HostSet;
+   unsigned int                TotalNumberOfInterfaces;
+
+   void dumpConfiguration(cTopology&      topo,
+                          NodeInfoVector& nodeInfo,
+                          const bool      intermediateVersion = false);
+};
+
+#endif
diff --git a/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.ned b/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.ned
new file mode 100644
index 0000000..5a6f8f8
--- /dev/null
+++ b/src/networklayer/autorouting/MultihomedFlatNetworkConfigurator.ned
@@ -0,0 +1,36 @@
+// * --------------------------------------------------------------------------
+// *
+// *     //====//  //===== <===//===>  //====//
+// *    //        //          //      //    //    SCTP Optimization Project
+// *   //=====   //          //      //====//   ==============================
+// *        //  //          //      //           University of Duisburg-Essen
+// *  =====//  //=====     //      //
+// *
+// * --------------------------------------------------------------------------
+// *
+// *   Copyright (C) 2009-2012 by Thomas Dreibholz
+// *
+// *   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 3 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+// *
+// *   Contact: dreibh@iem.uni-due.de
+
+
+package inet.networklayer.autorouting;
+
+
+simple MultihomedFlatNetworkConfigurator
+{
+    parameters:
+        @display("i=block/cogwheel_s");
+}
diff --git a/src/networklayer/ipv4/IP.cc b/src/networklayer/ipv4/IP.cc
index f3f9bf1..5099cec 100644
--- a/src/networklayer/ipv4/IP.cc
+++ b/src/networklayer/ipv4/IP.cc
@@ -1,5 +1,7 @@
 //
 // Copyright (C) 2004 Andras Varga
+// Copyright (C) 2007 Irene Ruengeler
+// Copyright (C) 2010-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public License
@@ -402,7 +404,22 @@ void IP::reassembleAndDeliver(IPDatagram *datagram)
     else
     {
         int gateindex = mapping.getOutputGateForProtocol(protocol);
-        send(packet, "transportOut", gateindex);
+        if (datagram->hasBitError())
+        {
+            packet->setBitError(true);
+            EV << "bit error in protocol "<< protocol << endl;
+            if (protocol!= IP_PROT_SCTP)
+            {
+                delete packet;
+            }
+            else
+            {
+                send(packet, "transportOut", gateindex);
+            }
+       }
+       else {
+            send(packet, "transportOut", gateindex);
+       }
     }
 }
 
diff --git a/src/networklayer/queue/DropTailQueue.cc b/src/networklayer/queue/DropTailQueue.cc
index 453033e..0c90c49 100644
--- a/src/networklayer/queue/DropTailQueue.cc
+++ b/src/networklayer/queue/DropTailQueue.cc
@@ -1,5 +1,6 @@
 //
 // Copyright (C) 2005 Andras Varga
+// Copyright (C) 2010-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public License
@@ -15,11 +16,23 @@
 // along with this program; if not, see <http://www.gnu.org/licenses/>.
 //
 
-
 #include <omnetpp.h>
+
+// ====== Activate debugging mode here! ===================
+// #define DTQUEUE_DEBUG                 // Print drops, queue status, etc.
+// #define DTQUEUE_DEBUG_FORWARDS        // Print all forwards
+// ========================================================
+
+#ifdef DTQUEUE_DEBUG
+#include "IPDatagram_m.h"
+#include "SCTPMessage.h"
+#include "SCTPAssociation.h"
+#define EV std::cout
+#endif
 #include "DropTailQueue.h"
 
 
+
 Define_Module(DropTailQueue);
 
 void DropTailQueue::initialize()
@@ -41,12 +54,18 @@ bool DropTailQueue::enqueue(cMessage *msg)
     if (frameCapacity && queue.length() >= frameCapacity)
     {
         EV << "Queue full, dropping packet.\n";
+#ifdef DTQUEUE_DEBUG
+        dumpInfo("DROPPING", msg);
+#endif
         delete msg;
         dropVec.record(1);
         return true;
     }
     else
     {
+#ifdef DTQUEUE_DEBUG_FORWARDS
+        dumpInfo("Queuing", msg);
+#endif
         queue.insert(msg);
         qlenVec.record(queue.length());
         return false;
@@ -58,7 +77,7 @@ cMessage *DropTailQueue::dequeue()
     if (queue.empty())
         return NULL;
 
-   cMessage *pk = (cMessage *)queue.pop();
+    cMessage *pk = (cMessage *)queue.pop();
 
     // statistics
     qlenVec.record(queue.length());
@@ -68,7 +87,44 @@ cMessage *DropTailQueue::dequeue()
 
 void DropTailQueue::sendOut(cMessage *msg)
 {
+#ifdef DTQUEUE_DEBUG_FORWARDS
+    dumpInfo("Sending", msg);
+    EV << endl;
+#endif
     send(msg, outGate);
 }
 
-
+#ifdef DTQUEUE_DEBUG
+// T.D. 03.03.2010: Print information on forwarded/dropped packets.
+//                  For SCTP, also the ports and DATA chunk TSNs are printed.
+void DropTailQueue::dumpInfo(const char* info, cMessage* msg)
+{
+   const IPDatagram*  ip      = dynamic_cast<const IPDatagram*>(msg);
+   SCTPMessage*       sctpMsg = dynamic_cast<SCTPMessage*>(ip->getEncapsulatedPacket());
+   if(sctpMsg) {
+      EV << simTime() << "\t" << getFullPath() << ":\t" << info << " SCTP message "
+         << ip->getSrcAddress()  << ":" << sctpMsg->getSrcPort()  << " - "
+         << ip->getDestAddress() << ":" << sctpMsg->getDestPort() << " -->";
+
+      for(uint32 i = 0;i < sctpMsg->getChunksArraySize();i++) {
+         const SCTPChunk* chunk = (const SCTPChunk*)sctpMsg->getChunks(i);
+         if(chunk->getChunkType() == DATA) {
+            const SCTPDataChunk* dataChunk = dynamic_cast<const SCTPDataChunk*>(chunk);
+            EV << "\t" << "DATA " << dataChunk->getTsn();
+         }
+         else if(chunk->getChunkType() == HEARTBEAT) {
+            EV << "\t" << "HEARTBEAT";
+         }
+         else if(chunk->getChunkType() == HEARTBEAT_ACK) {
+            EV << "\t" << "HEARTBEAT_ACK";
+         }
+      }
+   }
+   else {
+      EV << simTime() << ": " << info << " message "
+         << ip->getSrcAddress()  << " - "
+         << ip->getDestAddress() << "   ";
+   }
+   EV << "\tqueueLength=" << queue.length() << endl;
+}
+#endif
diff --git a/src/networklayer/queue/DropTailQueue.h b/src/networklayer/queue/DropTailQueue.h
index 5cb08d2..2b9a64a 100644
--- a/src/networklayer/queue/DropTailQueue.h
+++ b/src/networklayer/queue/DropTailQueue.h
@@ -45,7 +45,7 @@ class INET_API DropTailQueue : public PassiveQueueBase
     /**
      * Redefined from PassiveQueueBase.
      */
-    virtual bool enqueue(cMessage *msg);
+    virtual bool enqueue(cMessage* msg);
 
     /**
      * Redefined from PassiveQueueBase.
@@ -55,9 +55,14 @@ class INET_API DropTailQueue : public PassiveQueueBase
     /**
      * Redefined from PassiveQueueBase.
      */
-    virtual void sendOut(cMessage *msg);
-};
+    virtual void sendOut(cMessage* msg);
 
+#ifdef DTQUEUE_DEBUG
+    /**
+      * Print message information.
+      */
+    void dumpInfo(const char* info, cMessage* msg);
 #endif
+};
 
-
+#endif
diff --git a/src/networklayer/queue/REDQueue.cc b/src/networklayer/queue/REDQueue.cc
index f6a5dd5..de42f21 100644
--- a/src/networklayer/queue/REDQueue.cc
+++ b/src/networklayer/queue/REDQueue.cc
@@ -1,5 +1,6 @@
 //
 // Copyright (C) 2005 Andras Varga
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public License
@@ -15,8 +16,19 @@
 // along with this program; if not, see <http://www.gnu.org/licenses/>.
 //
 
-
 #include <omnetpp.h>
+
+// ====== Activate debugging mode here! =====================================
+// #define REDQUEUE_DEBUG             // Print drops, queue status, etc.
+// #define REDQUEUE_DEBUG_FORWARDS    // Print all forwards
+// ==========================================================================
+
+#ifdef REDQUEUE_DEBUG
+#include "IPDatagram_m.h"
+#include "SCTPMessage.h"
+#include "SCTPAssociation.h"
+#define EV std::cout
+#endif
 #include "REDQueue.h"
 
 
@@ -50,9 +62,27 @@ void REDQueue::initialize()
     WATCH(q_time);
     WATCH(count);
     WATCH(numEarlyDrops);
+
+#ifdef REDQUEUE_DEBUG
+    // print configuration
+    const cModule* ppp = outGate->getPathEndGate()->getOwnerModule();
+    if(ppp) {
+        const cGate* networkOutputGate = ppp->gate("phys$o");
+        if(networkOutputGate) {
+            const cModule* remoteSide = networkOutputGate->getPathEndGate()->getOwnerModule();
+            if(remoteSide) {
+                EV << "REDQueue from " << ppp->getFullPath() << " to "
+                          << remoteSide->getFullPath()
+                          << ":\twq=" << wq
+                          << "\tminth=" << minth << "\tmaxth=" << maxth
+                          << "\tmaxp=" << maxp << "\tpkrate=" << pkrate << endl;
+            }
+        }
+    }
+#endif
 }
 
-bool REDQueue::enqueue(cMessage *msg)
+bool REDQueue::enqueue(cMessage* msg)
 {
     //"
     // for each packet arrival
@@ -95,12 +125,37 @@ bool REDQueue::enqueue(cMessage *msg)
     //"
 
     bool mark = false;
-    if (minth<=avg && avg<maxth)
+    if ((minth <= avg) && (avg < maxth))   // avg in [minth,maxth)
     {
         count++;
-        double pb = maxp*(avg-minth) / (maxth-minth);
-        double pa = pb / (1-count*pb);
-        if (dblrand() < pa)
+        const double pb = maxp*(avg-minth) / (maxth-minth);
+        double pa;
+        if(count*pb >= 1) {
+           // T.D. 29.07.2011: This condition must be checked. Otherwise, pa
+           //                  may become negative => queue works like FIFO.
+           pa = 1.0;
+        }
+        else {
+           pa = pb / (1-count*pb);
+        }
+        const double r = dblrand();
+
+#if 0
+        const IPDatagram*  ip      = dynamic_cast<const IPDatagram*>(msg);
+        SCTPMessage*       sctpMsg = dynamic_cast<SCTPMessage*>(ip->getEncapsulatedPacket());
+        if(sctpMsg) {
+           std::cout << simTime() << "\t" << getFullPath() << ":\t" << " SCTP message "
+                     << ip->getSrcAddress()  << ":" << sctpMsg->getSrcPort()  << " - "
+                     << ip->getDestAddress() << ":" << sctpMsg->getDestPort() << " -->"
+                     << "\tpa="    << pa
+                     << "\tpb="    << pb
+                     << "\tcount=" << count
+                     << "\tr="     << r
+                     << "\tmark="  << (r < pa) << endl;
+        }
+#endif
+
+        if (r < pa)
         {
             EV << "Random early packet drop (avg queue len=" << avg << ", pa=" << pa << ")\n";
             mark = true;
@@ -108,9 +163,13 @@ bool REDQueue::enqueue(cMessage *msg)
             numEarlyDrops++;
         }
     }
-    else if (maxth <= avg)
+    else if ( (avg >= maxth) /* || (queue.length() >= maxth) */ )
+       // maxth is also the "hard" limit
     {
-        EV << "Avg queue len " << avg << " >= maxth, dropping packet.\n";
+       // T.D. 10.12.09: The hard limit must be checked here.
+       // When mark is set to "true", the count must be reset to 0!
+        EV << "Avg queue len " << avg << ", queue len "
+           << queue.length() << " => dropping packet.\n";
         mark = true;
         count = 0;
     }
@@ -120,14 +179,22 @@ bool REDQueue::enqueue(cMessage *msg)
     }
 
     // carry out decision
-    if (mark || queue.length()>=maxth) // maxth is also the "hard" limit
+    if (mark)
     {
+#ifdef REDQUEUE_DEBUG
+        dumpInfo("DROPPING", msg);
+        EV << " mark=" << (mark ? "yes" : "no") << endl;
+#endif
         delete msg;
         dropVec.record(1);
         return true;
     }
     else
     {
+#ifdef REDQUEUE_DEBUG_FORWARDS
+        dumpInfo("Queuing", msg);
+        EV << endl;
+#endif
         queue.insert(msg);
         qlenVec.record(queue.length());
         return false;
@@ -153,8 +220,12 @@ cMessage *REDQueue::dequeue()
     return pk;
 }
 
-void REDQueue::sendOut(cMessage *msg)
+void REDQueue::sendOut(cMessage* msg)
 {
+#ifdef REDQUEUE_DEBUG_FORWARDS
+    dumpInfo("Sending", msg);
+    EV << endl;
+#endif
     send(msg, outGate);
 }
 
@@ -163,3 +234,42 @@ void REDQueue::finish()
     PassiveQueueBase::finish();
     recordScalar("packets dropped early by RED", numEarlyDrops);
 }
+
+#ifdef REDQUEUE_DEBUG
+// T.D. 10.12.09: Print information on forwarded/dropped packets.
+//                For SCTP, also the ports and DATA chunk TSNs are printed.
+void REDQueue::dumpInfo(const char* info, cMessage* msg)
+{
+   const IPDatagram*  ip      = dynamic_cast<const IPDatagram*>(msg);
+   SCTPMessage*       sctpMsg = dynamic_cast<SCTPMessage*>(ip->getEncapsulatedPacket());
+   if(sctpMsg) {
+      EV << simTime() << "\t" << getFullPath() << ":\t" << info << " SCTP message "
+         << ip->getSrcAddress()  << ":" << sctpMsg->getSrcPort()  << " - "
+         << ip->getDestAddress() << ":" << sctpMsg->getDestPort() << " -->";
+
+      for(uint32 i = 0;i < sctpMsg->getChunksArraySize();i++) {
+         const SCTPChunk* chunk = (const SCTPChunk*)sctpMsg->getChunks(i);
+         if(chunk->getChunkType() == DATA) {
+            const SCTPDataChunk* dataChunk = dynamic_cast<const SCTPDataChunk*>(chunk);
+            EV << "\t" << "DATA " << dataChunk->getTsn();
+         }
+         else if(chunk->getChunkType() == HEARTBEAT) {
+            EV << "\t" << "HEARTBEAT";
+         }
+         else if(chunk->getChunkType() == HEARTBEAT_ACK) {
+            EV << "\t" << "HEARTBEAT_ACK";
+         }
+      }
+   }
+   else {
+      EV << simTime() << ": " << info << " message "
+         << ip->getSrcAddress()  << " - "
+         << ip->getDestAddress() << "   ";
+   }
+   EV << "\tqueueLength=" << queue.length()
+      << "\tavg=" << avg
+      << "\tminTh=" << minth
+      << "\tmaxTh=" << maxth
+      << "\tcount=" << count;
+}
+#endif
diff --git a/src/networklayer/queue/REDQueue.h b/src/networklayer/queue/REDQueue.h
index 660b376..df70f56 100644
--- a/src/networklayer/queue/REDQueue.h
+++ b/src/networklayer/queue/REDQueue.h
@@ -1,5 +1,6 @@
 //
 // Copyright (C) 2005 Andras Varga
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public License
@@ -22,6 +23,11 @@
 #include <omnetpp.h>
 #include "PassiveQueueBase.h"
 
+
+// Uncomment to print info on forwarded/dropped messages
+// #define REDQUEUE_DEBUG
+
+
 /**
  * RED queue. See NED for more info.
  */
@@ -56,7 +62,7 @@ class INET_API REDQueue : public PassiveQueueBase
     /**
      * Redefined from PassiveQueueBase.
      */
-    virtual bool enqueue(cMessage *msg);
+    virtual bool enqueue(cMessage* msg);
 
     /**
      * Redefined from PassiveQueueBase.
@@ -66,10 +72,14 @@ class INET_API REDQueue : public PassiveQueueBase
     /**
      * Redefined from PassiveQueueBase.
      */
-    virtual void sendOut(cMessage *msg);
+    virtual void sendOut(cMessage* msg);
 
+#ifdef REDQUEUE_DEBUG
+    /**
+      * Print message information.
+      */
+    void dumpInfo(const char* info, cMessage* msg);
+#endif
 };
 
 #endif
-
-
diff --git a/src/transport/contract/SCTPCommand.h b/src/transport/contract/SCTPCommand.h
index 556d5a5..fd4ad89 100644
--- a/src/transport/contract/SCTPCommand.h
+++ b/src/transport/contract/SCTPCommand.h
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 by Irene Ruengeler
-// Copyright (C) 2010 by Thomas Dreibholz
+// Copyright (C) 2012 by Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -28,20 +28,25 @@
 //
 enum SctpCommandCode
 {
-    SCTP_C_ASSOCIATE            = 1,        // active open (must carry SCTPOpenCommand)
-    SCTP_C_OPEN_PASSIVE         = 2,        // passive open (must carry SCTPOpenCommand)
-    SCTP_C_SEND                 = 3,        // send data (set on data packet)
-    SCTP_C_CLOSE                = 5,        // shutdown the association
-    SCTP_C_ABORT                = 6,        // abort connection
-    SCTP_C_STATUS               = 7,        // request status info (SCTP_I_STATUS) from SCTP
-    SCTP_C_RECEIVE              = 8,        // data receive request
-    SCTP_C_SEND_ORDERED         = 9,        // send data ordered
-    SCTP_C_SEND_UNORDERED       = 10,   // send data unordered
-    SCTP_C_PRIMARY              = 11,   // set primary path
-    SCTP_C_QUEUE_BYTES_LIMIT    = 12,   // set send queue limit (in bytes)
-    SCTP_C_QUEUE_MSGS_LIMIT     = 13,   // set send queue limit (in messages)
-    SCTP_C_SHUTDOWN             = 14,
-    SCTP_C_NO_OUTSTANDING       = 15
+	SCTP_C_ASSOCIATE         = 1,    // active open (must carry SCTPOpenCommand)
+	SCTP_C_OPEN_PASSIVE      = 2,    // passive open (must carry SCTPOpenCommand)
+	SCTP_C_SEND              = 3,    // send data (set on data packet)
+	SCTP_C_CLOSE             = 5,    // shutdown the association
+	SCTP_C_ABORT             = 6,    // abort connection
+	SCTP_C_STATUS            = 7,    // request status info (SCTP_I_STATUS) from SCTP
+	SCTP_C_RECEIVE           = 8,    // data receive request
+	SCTP_C_SEND_ORDERED      = 9,    // send data ordered
+	SCTP_C_SEND_UNORDERED    = 10,   // send data unordered
+	SCTP_C_PRIMARY           = 11,   // set primary path
+	SCTP_C_QUEUE_BYTES_LIMIT = 12,   // set send queue limit (in bytes)
+	SCTP_C_QUEUE_MSGS_LIMIT  = 13,   // set send queue limit (in messages)
+	SCTP_C_SHUTDOWN          = 14,
+	SCTP_C_NO_OUTSTANDING    = 15,
+	SCTP_C_STREAM_RESET      = 16,   // send StreamResetChunk
+	SCTP_C_STOP_SENDING      = 17,   // will be sent from M3UA
+	SCTP_C_NAT_INFO          = 18,
+	SCTP_C_SEND_ASCONF       = 19,
+	SCTP_C_SET_STREAM_PRIO   = 20
 };
 
 //
@@ -52,21 +57,26 @@ enum SctpCommandCode
 //
 enum SctpStatusInd
 {
-    SCTP_I_DATA                 = 1,                     // data packet (set on data packet)
-    SCTP_I_DATA_NOTIFICATION    = 2,     // data arrived notification
-    SCTP_I_ESTABLISHED          = 3,             // connection established
-    SCTP_I_PEER_CLOSED          = 4,             // FIN received from remote SCTP
-    SCTP_I_CLOSED               = 5,                 // connection closed normally (via FIN exchange)
-    SCTP_I_CONNECTION_REFUSED   = 6, // connection refused
-    SCTP_I_CONNECTION_RESET     = 7,     // connection reset
-    SCTP_I_TIMED_OUT            = 8,             // conn-estab timer went off, or max retransm. count reached
-    SCTP_I_STATUS               = 9,                 // status info (will carry SCTPStatusInfo)
-    SCTP_I_ABORT                = 10,                // association was aborted by the peer
-    SCTP_I_CONN_LOST            = 11,            // association had too many retransmissions FIXME
-    SCTP_I_SEND_MSG             = 12,
-    SCTP_I_SHUTDOWN_RECEIVED    = 13,
-    SCTP_I_SENDQUEUE_FULL       = 14,
-    SCTP_I_SENDQUEUE_ABATED     = 15
+	SCTP_I_DATA = 1,               // data packet (set on data packet)
+	SCTP_I_DATA_NOTIFICATION = 2,  // data arrived notification
+	SCTP_I_ESTABLISHED = 3,        // connection established
+	SCTP_I_PEER_CLOSED = 4,        // FIN received from remote SCTP
+	SCTP_I_CLOSED = 5,             // connection closed normally (via FIN exchange)
+	SCTP_I_CONNECTION_REFUSED = 6, // connection refused
+	SCTP_I_CONNECTION_RESET = 7,   // connection reset
+	SCTP_I_TIMED_OUT = 8,          // conn-estab timer went off, or max retransm. count reached
+	SCTP_I_STATUS = 9,             // status info (will carry SCTPStatusInfo)
+	SCTP_I_ABORT = 10,             // association was aborted by the peer
+	SCTP_I_CONN_LOST = 11,         // association had too many retransmissions FIXME
+	SCTP_I_SEND_MSG = 12,
+	SCTP_I_SHUTDOWN_RECEIVED = 13,
+	SCTP_I_SENDQUEUE_FULL = 14,
+	SCTP_I_SENDQUEUE_ABATED = 15,
+	SCTP_I_ABANDONED = 16,
+	SCTP_I_SEND_STREAMS_RESETTED = 17,
+	SCTP_I_RCV_STREAMS_RESETTED = 18,
+	SCTP_I_RESET_REQUEST_FAILED = 19,
+	SCTP_I_ADDRESS_ADDED = 20 // used for AddIP and multihomed NAT
 };
 
 
@@ -75,5 +85,3 @@ enum SctpStatusInd
 //
 
 #endif
-
-
diff --git a/src/transport/contract/SCTPCommand.msg b/src/transport/contract/SCTPCommand.msg
index fa384de..a51cc74 100644
--- a/src/transport/contract/SCTPCommand.msg
+++ b/src/transport/contract/SCTPCommand.msg
@@ -56,7 +56,7 @@ class SCTPCommand extends cPacket
     int numMsgs = 1;
     int ssn = -1;
     unsigned short sendUnordered = false;
-    double lifetime = 0;
+    double prValue = 0;
     IPvXAddress localAddr = IPvXAddress("0.0.0.0");
     IPvXAddress remoteAddr = IPvXAddress("0.0.0.0");
     int gate = -1;
@@ -110,6 +110,8 @@ class SCTPOpenCommand extends SCTPCommand
     string sctpAlgorithmClass;       // may be left empty
     uint32 inboundStreams;
     uint32 outboundStreams;
+    bool streamReset;
+    int prMethod;
     uint32 numRequests;
     uint32 messagesToPush;
 }
@@ -122,9 +124,11 @@ class SCTPOpenCommand extends SCTPCommand
 //
 class SCTPSendCommand extends SCTPCommand
 {
+    unsigned int prMethod;
     bool last;
     unsigned int ppid = 0;
     bool primary = true;
+    bool sackNow = false;
 }
 
 
@@ -156,10 +160,10 @@ class SCTPConnectInfo extends SCTPCommand
 //
 class SCTPStatusInfo extends SCTPCommand
 {
-    int state;
-    string stateName;
+    int         state;
+    string      stateName;
     IPvXAddress pathId;
-    bool active;
+    bool        active;
 }
 
 class SCTPPathInfo extends SCTPCommand
@@ -181,7 +185,15 @@ class SCTPInfo extends SCTPCommand
 
 class SCTPRcvCommand extends SCTPCommand
 {
-    uint32 ppid;
-    uint32 tsn;
-    uint32 cumTsn;
+	uint32 ppid;
+	uint32 tsn;
+	uint32 cumTsn;
+}
+
+class SCTPSendQueueAbated extends SCTPCommand
+{
+   uint64 bytesAvailable;
+   uint64 bytesQueued;
+   uint64 bytesLimit;
+   uint64 queuedForStream[];
 }
diff --git a/src/transport/contract/SCTPSocket.cc b/src/transport/contract/SCTPSocket.cc
index 8d9dc9f..3924484 100644
--- a/src/transport/contract/SCTPSocket.cc
+++ b/src/transport/contract/SCTPSocket.cc
@@ -22,373 +22,465 @@
 
 SCTPSocket::SCTPSocket(bool type)
 {
-    sockstate = NOT_BOUND;
-    localPrt = remotePrt = 0;
-    cb = NULL;
-    yourPtr = NULL;
-    gateToSctp = NULL;
-    lastStream=-1;
-    oneToOne = type;
-    if (oneToOne)
-        assocId = SCTP::getNewConnId();
-    else
-        assocId = 0;
-    sctpEV3<<"sockstate="<<sockstate<<"\n";
+   sockstate = NOT_BOUND;
+   localPrt = remotePrt = 0;
+   cb = NULL;
+   yourPtr = NULL;
+   gateToSctp = NULL;
+   lastStream=-1;
+   oneToOne = type;
+   if (oneToOne)
+      assocId = SCTP::getNewAssocId();
+   else
+      assocId = 0;
+   sctpEV3<<"sockstate="<<sockstate<<"\n";
 }
 
 SCTPSocket:: ~SCTPSocket()
 {
-    localAddresses.clear();
+   localAddresses.clear();
 }
 
 const char *SCTPSocket::stateName(int state)
 {
 #define CASE(x) case x: s=#x; break
-    const char *s = "unknown";
-    switch (state)
-    {
-        CASE(NOT_BOUND);
-        CASE(CLOSED);
-        CASE(LISTENING);
-        CASE(CONNECTING);
-        CASE(CONNECTED);
-        CASE(PEER_CLOSED);
-        CASE(LOCALLY_CLOSED);
-        CASE(SOCKERROR);
-    }
-    return s;
+   const char *s = "unknown";
+   switch (state)
+   {
+      CASE(NOT_BOUND);
+      CASE(CLOSED);
+      CASE(LISTENING);
+      CASE(CONNECTING);
+      CASE(CONNECTED);
+      CASE(PEER_CLOSED);
+      CASE(LOCALLY_CLOSED);
+      CASE(SOCKERROR);
+   }
+   return s;
 #undef CASE
 }
 
 void SCTPSocket::sendToSCTP(cPacket *msg)
 {
-    if (!gateToSctp)
-        opp_error("SCTPSocket: setOutputGate() must be invoked before socket can be used");
-    check_and_cast<cSimpleModule *>(gateToSctp->getOwnerModule())->send(msg, gateToSctp);
+   if (!gateToSctp)
+      opp_error("SCTPSocket: setOutputGate() must be invoked before socket can be used");
+   check_and_cast<cSimpleModule *>(gateToSctp->getOwnerModule())->send(msg, gateToSctp);
 }
 
 void SCTPSocket::bind(int lPort)
 {
-    if (sockstate!=NOT_BOUND)
-        opp_error("SCTPSocket::bind(): socket already bound");
-    localAddresses.push_back(IPvXAddress("0.0.0.0"));
-    localPrt = lPort;
-    sockstate = CLOSED;
+   if (sockstate!=NOT_BOUND)
+      opp_error("SCTPSocket::bind(): socket already bound");
+   localAddresses.push_back(IPvXAddress("0.0.0.0"));
+   localPrt = lPort;
+   sockstate = CLOSED;
 }
 
 void SCTPSocket::bind(IPvXAddress lAddr, int lPort)
 {
-    sctpEV3<<"bind address "<<lAddr<<"\n";
-    if (sockstate!=NOT_BOUND)
-        opp_error("SCTPSocket::bind(): socket already bound");
-    localAddresses.push_back(lAddr);
-    localPrt = lPort;
-    sockstate = CLOSED;
+   sctpEV3<<"bind address "<<lAddr<<"\n";
+   if (sockstate!=NOT_BOUND)
+      opp_error("SCTPSocket::bind(): socket already bound");
+   localAddresses.push_back(lAddr);
+   localPrt = lPort;
+   sockstate = CLOSED;
 }
 
 void SCTPSocket::addAddress(IPvXAddress addr)
 {
-    sctpEV3<<"add address "<<addr<<"\n";
-    localAddresses.push_back(addr);
+   sctpEV3<<"add address "<<addr<<"\n";
+   localAddresses.push_back(addr);
 }
 
 void SCTPSocket::bindx(AddressVector lAddresses, int lPort)
 {
-    IPvXAddress lAddr;
-    for (AddressVector::iterator i=lAddresses.begin(); i!=lAddresses.end(); ++i)
-    {
-        ev<<"bindx: bind address "<<(*i)<<"\n";
-        localAddresses.push_back((*i));
-    }
-    localPrt = lPort;
-    sockstate = CLOSED;
+   IPvXAddress lAddr;
+   for (AddressVector::iterator i=lAddresses.begin(); i!=lAddresses.end(); ++i)
+   {
+      ev<<"bindx: bind address "<<(*i)<<"\n";
+      localAddresses.push_back((*i));
+   }
+   localPrt = lPort;
+   sockstate = CLOSED;
 }
 
-void SCTPSocket::listen(bool fork, uint32 requests, uint32 messagesToPush)
+void SCTPSocket::listen(bool fork, bool reset, uint32 requests, uint32 messagesToPush)
 {
-    if (sockstate!=CLOSED)
-        opp_error(sockstate==NOT_BOUND ? "SCTPSocket: must call bind() before listen()"
-                                       : "SCTPSocket::listen(): connect() or listen() already called");
-
-    cPacket *msg = new cPacket("PassiveOPEN", SCTP_C_OPEN_PASSIVE);
-
-    SCTPOpenCommand *openCmd = new SCTPOpenCommand();
-    //openCmd->setLocalAddr(localAddr);
-    openCmd->setLocalAddresses(localAddresses);
-    openCmd->setLocalPort(localPrt);
-    if (oneToOne)
-        openCmd->setAssocId(assocId);
-    else
-        openCmd->setAssocId(SCTP::getNewConnId());
-    openCmd->setFork(fork);
+   if (sockstate!=CLOSED)
+      opp_error(sockstate==NOT_BOUND ? "SCTPSocket: must call bind() before listen()"
+                                     : "SCTPSocket::listen(): connect() or listen() already called");
+
+   cPacket *msg = new cPacket("PassiveOPEN", SCTP_C_OPEN_PASSIVE);
+
+   SCTPOpenCommand *openCmd = new SCTPOpenCommand();
+   //openCmd->setLocalAddr(localAddr);
+   openCmd->setLocalAddresses(localAddresses);
+   openCmd->setLocalPort(localPrt);
+   if (oneToOne)
+      openCmd->setAssocId(assocId);
+   else
+      openCmd->setAssocId(SCTP::getNewAssocId());
+   openCmd->setFork(fork);
+   openCmd->setOutboundStreams(outboundStreams);
    openCmd->setInboundStreams(inboundStreams);
-    openCmd->setOutboundStreams(outboundStreams);
-    openCmd->setNumRequests(requests);
-    openCmd->setMessagesToPush(messagesToPush);
-    msg->setControlInfo(openCmd);
-    sctpEV3<<"Assoc "<<openCmd->getAssocId()<<"::send PassiveOPEN to SCTP from socket:listen \n";
-
-    sendToSCTP(msg);
-    sockstate = LISTENING;
+   openCmd->setNumRequests(requests);
+   openCmd->setStreamReset(reset);
+   openCmd->setMessagesToPush(messagesToPush);
+   msg->setControlInfo(openCmd);
+   sctpEV3<<"Assoc "<<openCmd->getAssocId()<<"::send PassiveOPEN to SCTP from socket:listen \n";
+
+   sendToSCTP(msg);
+   sockstate = LISTENING;
 }
 
-void SCTPSocket::connect(IPvXAddress remoteAddress, int32 remotePort, uint32 numRequests)
+void SCTPSocket::connect(IPvXAddress remoteAddress, int32 remotePort, bool streamReset, int32 prMethod, uint32 numRequests)
 {
 sctpEV3<<"Socket connect. Assoc="<<assocId<<", sockstate="<<sockstate<<"\n";
-    if (oneToOne && sockstate!=NOT_BOUND && sockstate!=CLOSED)
-        opp_error( "SCTPSocket::connect(): connect() or listen() already called");
-    else if (!oneToOne && sockstate!=LISTENING)
-        opp_error( "SCTPSocket::connect: One-to-many style socket must be listening");
-    cPacket *msg = new cPacket("Associate", SCTP_C_ASSOCIATE);
-    remoteAddr = remoteAddress;
-    remotePrt = remotePort;
-    SCTPOpenCommand *openCmd = new SCTPOpenCommand();
-    if (oneToOne)
-        openCmd->setAssocId(assocId);
-    else
-        openCmd->setAssocId(SCTP::getNewConnId());
-    sctpEV3<<"Socket connect. Assoc="<<openCmd->getAssocId()<<", sockstate="<<stateName(sockstate)<<"\n";
-    //openCmd->setAssocId(assocId);
-    openCmd->setLocalAddresses(localAddresses);
-    openCmd->setLocalPort(localPrt);
-    openCmd->setRemoteAddr(remoteAddr);
-    openCmd->setRemotePort(remotePrt);
-    openCmd->setOutboundStreams(outboundStreams);
-   openCmd->setOutboundStreams(inboundStreams);
-    openCmd->setNumRequests(numRequests);
-    msg->setControlInfo(openCmd);
-    sendToSCTP(msg);
-    if (oneToOne)
-        sockstate = CONNECTING;
+   if (oneToOne && sockstate!=NOT_BOUND && sockstate!=CLOSED)
+      opp_error( "SCTPSocket::connect(): connect() or listen() already called");
+   else if (!oneToOne && sockstate!=LISTENING)
+      opp_error( "SCTPSocket::connect: One-to-many style socket must be listening");
+   cPacket *msg = new cPacket("Associate", SCTP_C_ASSOCIATE);
+   remoteAddr = remoteAddress;
+   remotePrt = remotePort;
+   SCTPOpenCommand *openCmd = new SCTPOpenCommand();
+   if (oneToOne)
+      openCmd->setAssocId(assocId);
+   else
+      openCmd->setAssocId(SCTP::getNewAssocId());
+   sctpEV3<<"Socket connect. Assoc="<<openCmd->getAssocId()<<", sockstate="<<stateName(sockstate)<<"\n";
+   //openCmd->setAssocId(assocId);
+   openCmd->setLocalAddresses(localAddresses);
+   openCmd->setLocalPort(localPrt);
+   openCmd->setRemoteAddr(remoteAddr);
+   openCmd->setRemotePort(remotePrt);
+   openCmd->setOutboundStreams(outboundStreams);
+   openCmd->setInboundStreams(inboundStreams);
+   openCmd->setNumRequests(numRequests);
+   openCmd->setPrMethod(prMethod);
+   openCmd->setStreamReset(streamReset);
+   msg->setControlInfo(openCmd);
+   sendToSCTP(msg);
+   if (oneToOne)
+      sockstate = CONNECTING;
 }
 
+void SCTPSocket::connect(IPvXAddress remoteAddress, int32 remotePort)
+{
+   if (oneToOne && sockstate!=NOT_BOUND && sockstate!=CLOSED)
+      opp_error( "SCTPSocket::connect(): connect() or listen() already called");
+   else if (!oneToOne && sockstate!=LISTENING)
+      opp_error( "SCTPSocket::connect: One-to-many style socket must be listening");
+   cPacket *msg = new cPacket("Associate", SCTP_C_ASSOCIATE);
+   remoteAddr = remoteAddress;
+   remotePrt = remotePort;
+   SCTPOpenCommand *openCmd = new SCTPOpenCommand();
+   if (oneToOne)
+      openCmd->setAssocId(assocId);
+   else
+      openCmd->setAssocId(SCTP::getNewAssocId());
+   sctpEV3<<"Socket connect. Assoc="<<openCmd->getAssocId()<<", sockstate="<<stateName(sockstate)<<"\n";
+   openCmd->setLocalAddresses(localAddresses);
+   openCmd->setLocalPort(localPrt);
+   openCmd->setRemoteAddr(remoteAddr);
+   openCmd->setRemotePort(remotePrt);
+   openCmd->setOutboundStreams(outboundStreams);
+   openCmd->setInboundStreams(inboundStreams);
+   openCmd->setNumRequests(0);
+   openCmd->setPrMethod(0);
+   openCmd->setStreamReset(0);
+   msg->setControlInfo(openCmd);
+   sendToSCTP(msg);
+   if (oneToOne)
+      sockstate = CONNECTING;
+}
 
-void SCTPSocket::connectx(AddressVector remoteAddressList, int32 remotePort, uint32 numRequests)
+void SCTPSocket::connectx(AddressVector remoteAddressList, int32 remotePort, bool streamReset, int32 prMethod, uint32 numRequests)
 {
-    sctpEV3<<"Socket connectx.  sockstate="<<sockstate<<"\n";
-    /*if (sockstate!=NOT_BOUND && sockstate!=CLOSED)
-        opp_error( "SCTPSocket::connect(): connect() or listen() already called");*/
-    if (oneToOne && sockstate!=NOT_BOUND && sockstate!=CLOSED)
-        opp_error( "SCTPSocket::connect(): connect() or listen() already called");
-    else if (!oneToOne && sockstate!=LISTENING)
-        opp_error( "SCTPSocket::connect: One-to-many style socket must be listening");
-    cPacket *msg = new cPacket("Associate", SCTP_C_ASSOCIATE);
-    remoteAddresses = remoteAddressList;
-    remoteAddr = remoteAddresses.front();
-    remotePrt = remotePort;
-    SCTPOpenCommand *openCmd = new SCTPOpenCommand();
-    openCmd->setAssocId(assocId);
-    openCmd->setLocalAddresses(localAddresses);
-    openCmd->setLocalPort(localPrt);
-    openCmd->setRemoteAddr(remoteAddr);
-    openCmd->setRemoteAddresses(remoteAddresses);
-    openCmd->setRemotePort(remotePrt);
-    openCmd->setOutboundStreams(outboundStreams);
-    openCmd->setNumRequests(numRequests);
-    msg->setControlInfo(openCmd);
-    sendToSCTP(msg);
-    if (oneToOne)
-        sockstate = CONNECTING;
+   sctpEV3<<"Socket connectx.  sockstate="<<sockstate<<"\n";
+   if (oneToOne && sockstate!=NOT_BOUND && sockstate!=CLOSED)
+      opp_error( "SCTPSocket::connect(): connect() or listen() already called");
+   else if (!oneToOne && sockstate!=LISTENING)
+      opp_error( "SCTPSocket::connect: One-to-many style socket must be listening");
+   cPacket *msg = new cPacket("Associate", SCTP_C_ASSOCIATE);
+   remoteAddresses = remoteAddressList;
+   remoteAddr = remoteAddresses.front();
+   remotePrt = remotePort;
+   SCTPOpenCommand *openCmd = new SCTPOpenCommand();
+   openCmd->setAssocId(assocId);
+   openCmd->setLocalAddresses(localAddresses);
+   openCmd->setLocalPort(localPrt);
+   openCmd->setRemoteAddr(remoteAddr);
+   openCmd->setRemoteAddresses(remoteAddresses);
+   openCmd->setRemotePort(remotePrt);
+   openCmd->setOutboundStreams(outboundStreams);
+   openCmd->setInboundStreams(inboundStreams);
+   openCmd->setNumRequests(numRequests);
+   openCmd->setPrMethod(prMethod);
+   openCmd->setStreamReset(streamReset);
+   msg->setControlInfo(openCmd);
+   sendToSCTP(msg);
+   if (oneToOne)
+      sockstate = CONNECTING;
 }
 
 void SCTPSocket::send(cPacket *msg, bool last, bool primary)
 {
-    if (oneToOne && sockstate!=CONNECTED && sockstate!=CONNECTING && sockstate!=PEER_CLOSED) {
-        opp_error("SCTPSocket::send(): not connected or connecting");
+   if (oneToOne && sockstate!=CONNECTED && sockstate!=CONNECTING && sockstate!=PEER_CLOSED) {
+      opp_error("SCTPSocket::send(): not connected or connecting");
    }
-    else if (!oneToOne && sockstate!=LISTENING) {
-        opp_error( "SCTPSocket::send: One-to-many style socket must be listening");
+   else if (!oneToOne && sockstate!=LISTENING) {
+      opp_error( "SCTPSocket::send: One-to-many style socket must be listening");
    }
-    SCTPSendCommand *cmd = new SCTPSendCommand();
-    cmd->setAssocId(assocId);
-    if (msg->getKind() == SCTP_C_SEND_ORDERED)
-        cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
-    else
-        cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
-    lastStream=(lastStream+1)%outboundStreams;
-    cmd->setSid(lastStream);
-    cmd->setLast(last);
-    cmd->setPrimary(primary);
-    msg->setKind(SCTP_C_SEND);
-    msg->setControlInfo(cmd);
-    sendToSCTP(msg);
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(assocId);
+   if (msg->getKind() == SCTP_C_SEND_ORDERED)
+      cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
+   else
+      cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
+   lastStream=(lastStream+1)%outboundStreams;
+   cmd->setSid(lastStream);
+   cmd->setLast(last);
+   cmd->setPrimary(primary);
+   msg->setKind(SCTP_C_SEND);
+   msg->setControlInfo(cmd);
+   sendToSCTP(msg);
 }
 
+void SCTPSocket::send(cPacket *msg, int32 prMethod, double prValue, bool last)
+{
+   send(msg, prMethod, prValue, last, -1);
+}
+
+void SCTPSocket::send(cPacket *msg, int32 prMethod, double prValue, bool last, int32 streamId)
+{
+   if (oneToOne && sockstate!=CONNECTED && sockstate!=CONNECTING && sockstate!=PEER_CLOSED) {
+      opp_error("SCTPSocket::send(): not connected or connecting");
+   }
+   else if (!oneToOne && sockstate!=LISTENING) {
+      opp_error( "SCTPSocket::send: One-to-many style socket must be listening");
+   }
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(assocId);
+   if (msg->getKind() == SCTP_C_SEND_ORDERED)
+      cmd->setSendUnordered(COMPLETE_MESG_ORDERED);
+   else
+      cmd->setSendUnordered(COMPLETE_MESG_UNORDERED);
+   if (streamId >= 0)
+   {
+      cmd->setSid(streamId);
+   }
+   else
+   {
+      lastStream=(lastStream+1)%outboundStreams;
+      cmd->setSid(lastStream);
+   }
+   cmd->setPrValue(prValue);
+   cmd->setPrMethod(prMethod);
+   cmd->setLast(last);
+   msg->setKind(SCTP_C_SEND);
+   msg->setControlInfo(cmd);
+   sendToSCTP(msg);
+}
 
 void SCTPSocket::sendNotification(cPacket *msg)
 {
-    if (oneToOne && sockstate!=CONNECTED && sockstate!=CONNECTING && sockstate!=PEER_CLOSED) {
-        opp_error("SCTPSocket::sendNotification(%s): not connected or connecting", msg->getName());
+   if (oneToOne && sockstate!=CONNECTED && sockstate!=CONNECTING && sockstate!=PEER_CLOSED) {
+      opp_error("SCTPSocket::sendNotification(%s): not connected or connecting", msg->getName());
    }
-    else if (!oneToOne && sockstate!=LISTENING) {
-        opp_error( "SCTPSocket::send: One-to-many style socket must be listening");
+   else if (!oneToOne && sockstate!=LISTENING) {
+      opp_error( "SCTPSocket::send: One-to-many style socket must be listening");
    }
-    sendToSCTP(msg);
+   sendToSCTP(msg);
 }
 
 void SCTPSocket::sendRequest(cPacket *msg)
 {
-    sendToSCTP(msg);
+   sendToSCTP(msg);
 }
 
 void SCTPSocket::close()
 {
-    sctpEV3<<"SCTPSocket: close\n";
-
-    cPacket *msg = new cPacket("CLOSE", SCTP_C_CLOSE);
-    SCTPCommand *cmd = new SCTPCommand();
-    cmd->setAssocId(assocId);
-    msg->setControlInfo(cmd);
-    sendToSCTP(msg);
-    sockstate = sockstate==CONNECTED ? LOCALLY_CLOSED : CLOSED;
+   sctpEV3<<"SCTPSocket: close\n";
+
+   cPacket *msg = new cPacket("CLOSE", SCTP_C_CLOSE);
+   SCTPCommand *cmd = new SCTPCommand();
+   cmd->setAssocId(assocId);
+   msg->setControlInfo(cmd);
+   sendToSCTP(msg);
+   sockstate = sockstate==CONNECTED ? LOCALLY_CLOSED : CLOSED;
 }
 
 void SCTPSocket::shutdown()
 {
-    ev<<"SCTPSocket: shutdown\n";
+   ev<<"SCTPSocket: shutdown\n";
 
-    cPacket *msg = new cPacket("Shutdown", SCTP_C_SHUTDOWN);
-    SCTPCommand *cmd = new SCTPCommand();
-    cmd->setAssocId(assocId);
-    msg->setControlInfo(cmd);
-    sendToSCTP(msg);
+   cPacket *msg = new cPacket("Shutdown", SCTP_C_SHUTDOWN);
+   SCTPCommand *cmd = new SCTPCommand();
+   cmd->setAssocId(assocId);
+   msg->setControlInfo(cmd);
+   sendToSCTP(msg);
 }
 
 void SCTPSocket::abort()
 {
-    if (sockstate!=NOT_BOUND && sockstate!=CLOSED && sockstate!=SOCKERROR)
-    {
-        cPacket *msg = new cPacket("ABORT", SCTP_C_ABORT);
-        SCTPCommand *cmd = new SCTPCommand();
-        //sctpEV3<<"Message cmd="<<&cmd<<"\n";
-        cmd->setAssocId(assocId);
-        msg->setControlInfo(cmd);
-        sendToSCTP(msg);
-    }
-    sockstate = CLOSED;
+   if (sockstate!=NOT_BOUND && sockstate!=CLOSED && sockstate!=SOCKERROR)
+   {
+      cPacket *msg = new cPacket("ABORT", SCTP_C_ABORT);
+      SCTPCommand *cmd = new SCTPCommand();
+      //sctpEV3<<"Message cmd="<<&cmd<<"\n";
+      cmd->setAssocId(assocId);
+      msg->setControlInfo(cmd);
+      sendToSCTP(msg);
+   }
+   sockstate = CLOSED;
 }
 
 void SCTPSocket::requestStatus()
 {
-    cPacket *msg = new cPacket("STATUS", SCTP_C_STATUS);
-    SCTPCommand *cmd = new SCTPCommand();
-    cmd->setAssocId(assocId);
-    msg->setControlInfo(cmd);
-    sendToSCTP(msg);
+   cPacket *msg = new cPacket("STATUS", SCTP_C_STATUS);
+   SCTPCommand *cmd = new SCTPCommand();
+   cmd->setAssocId(assocId);
+   msg->setControlInfo(cmd);
+   sendToSCTP(msg);
 }
 
 bool SCTPSocket::belongsToSocket(cPacket *msg)
 {
-    bool ret= dynamic_cast<SCTPCommand *>(msg->getControlInfo()) &&
+   bool ret= dynamic_cast<SCTPCommand *>(msg->getControlInfo()) &&
            ((SCTPCommand *)(msg->getControlInfo()))->getAssocId()==assocId;
-    sctpEV3<<"assoc="<<((SCTPCommand *)(msg->getControlInfo()))->getAssocId()<<"\n";
-    return ret;
+   sctpEV3<<"assoc="<<((SCTPCommand *)(msg->getControlInfo()))->getAssocId()<<"\n";
+   return ret;
 }
 
 bool SCTPSocket::belongsToAnySCTPSocket(cPacket *msg)
 {
-    return dynamic_cast<SCTPCommand *>(msg->getControlInfo());
+   return dynamic_cast<SCTPCommand *>(msg->getControlInfo());
 }
 
 void SCTPSocket::setCallbackObject(CallbackInterface *callback, void *yourPointer)
 {
-    cb = callback;
-    yourPtr = yourPointer;
+   cb = callback;
+   yourPtr = yourPointer;
 }
 
 void SCTPSocket::processMessage(cPacket *msg)
 {
-    SCTPStatusInfo *status;
-    switch (msg->getKind())
-    {
-        case SCTP_I_DATA:
-            sctpEV3<<"SCTP_I_DATA\n";
-            if (cb)
-                cb->socketDataArrived(assocId, yourPtr, msg, false);
-            break;
-        case SCTP_I_DATA_NOTIFICATION:
-            sctpEV3<<"SCTP_I_NOTIFICATION\n";
-            if (cb)
-                cb->socketDataNotificationArrived(assocId, yourPtr, msg);
-            break;
-        case SCTP_I_SEND_MSG:
-            if (cb)
-                cb->sendRequestArrived();
-            break;
-        case SCTP_I_ESTABLISHED:
-        {
-            if (oneToOne)
-                sockstate = CONNECTED;
-            SCTPConnectInfo *connectInfo = check_and_cast<SCTPConnectInfo *>(msg->removeControlInfo());
-            localAddr = connectInfo->getLocalAddr();
-            remoteAddr = connectInfo->getRemoteAddr();
-            localPrt = connectInfo->getLocalPort();
-            remotePrt = connectInfo->getRemotePort();;
-            fsmStatus = connectInfo->getStatus();
-            inboundStreams = connectInfo->getInboundStreams();
-            outboundStreams = connectInfo->getOutboundStreams();
-
-            if (cb)
-                cb->socketEstablished(assocId, yourPtr, connectInfo->getNumMsgs());
-            delete connectInfo;
-            break;
-        }
-        case SCTP_I_PEER_CLOSED:
-            sctpEV3<<"peer closed\n";
-            if (oneToOne)
-                sockstate = sockstate==CONNECTED ? PEER_CLOSED : CLOSED;
-
-            if (cb)
-                cb->socketPeerClosed(assocId, yourPtr);
-            break;
-        case SCTP_I_ABORT:
-        case SCTP_I_CONN_LOST:
-        case SCTP_I_CLOSED:
-            sctpEV3<<"SCTP_I_CLOSED called\n";
-            sockstate = CLOSED;
-
-            if (cb)
-                cb->socketClosed(assocId, yourPtr);
-            break;
-        case SCTP_I_CONNECTION_REFUSED:
-        case SCTP_I_CONNECTION_RESET:
-        case SCTP_I_TIMED_OUT:
-            sockstate = SOCKERROR;
-            if (cb)
-                cb->socketFailure(assocId, yourPtr, msg->getKind());
-
-            break;
-        case SCTP_I_STATUS:
-            status = check_and_cast<SCTPStatusInfo *>(msg->removeControlInfo());
-
-            if (cb)
-                cb->socketStatusArrived(assocId, yourPtr, status);
-            delete status;
-            break;
-        case SCTP_I_SHUTDOWN_RECEIVED:
-            sctpEV3<<"SCTP_I_SHUTDOWN_RECEIVED\n";
-        if (cb)
-                cb->shutdownReceivedArrived(assocId);
-            break;
-        case SCTP_I_SENDQUEUE_FULL:
-            if (cb)
-                cb->sendqueueFullArrived(assocId);
-            break;
-        case SCTP_I_SENDQUEUE_ABATED:
-        {
-            SCTPCommand *cmd = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
-            if (cb)
-            {
-                cb->sendqueueAbatedArrived(assocId, cmd->getNumMsgs());
-            }
-            delete cmd;
-            break;
-        }
-        default:
-            opp_error("SCTPSocket: invalid msg kind %d, one of the SCTP_I_xxx constants expected", msg->getKind());
-    }
-
-    delete msg;
-}
+   SCTPStatusInfo *status;
+   switch (msg->getKind())
+   {
+      case SCTP_I_DATA:
+         sctpEV3<<"SCTP_I_DATA\n";
+         if (cb)
+            cb->socketDataArrived(assocId, yourPtr, msg, false);
+         break;
+      case SCTP_I_DATA_NOTIFICATION:
+         sctpEV3<<"SCTP_I_NOTIFICATION\n";
+         if (cb)
+            cb->socketDataNotificationArrived(assocId, yourPtr, msg);
+         break;
+      case SCTP_I_SEND_MSG:
+         if (cb)
+            cb->sendRequestArrived();
+         break;
+      case SCTP_I_ESTABLISHED:
+      {
+         if (oneToOne)
+            sockstate = CONNECTED;
+         SCTPConnectInfo *connectInfo = check_and_cast<SCTPConnectInfo *>(msg->removeControlInfo());
+         localAddr = connectInfo->getLocalAddr();
+         remoteAddr = connectInfo->getRemoteAddr();
+         localPrt = connectInfo->getLocalPort();
+         remotePrt = connectInfo->getRemotePort();;
+         fsmStatus = connectInfo->getStatus();
+         inboundStreams = connectInfo->getInboundStreams();
+         outboundStreams = connectInfo->getOutboundStreams();
+
+         if (cb)
+            cb->socketEstablished(assocId, yourPtr, connectInfo->getNumMsgs());
+         delete connectInfo;
+         break;
+      }
+      case SCTP_I_PEER_CLOSED:
+         sctpEV3<<"peer closed\n";
+         if (oneToOne)
+            sockstate = sockstate==CONNECTED ? PEER_CLOSED : CLOSED;
+
+         if (cb)
+            cb->socketPeerClosed(assocId, yourPtr);
+         break;
+      case SCTP_I_ABORT:
+      case SCTP_I_CONN_LOST:
+      case SCTP_I_CLOSED:
+         sctpEV3<<"SCTP_I_CLOSED called\n";
+         sockstate = CLOSED;
+
+         if (cb)
+            cb->socketClosed(assocId, yourPtr);
+         break;
+      case SCTP_I_CONNECTION_REFUSED:
+      case SCTP_I_CONNECTION_RESET:
+      case SCTP_I_TIMED_OUT:
+         sockstate = SOCKERROR;
+         if (cb)
+            cb->socketFailure(assocId, yourPtr, msg->getKind());
+
+         break;
+      case SCTP_I_STATUS:
+         status = check_and_cast<SCTPStatusInfo *>(msg->removeControlInfo());
+
+         if (cb)
+            cb->socketStatusArrived(assocId, yourPtr, status);
+         delete status;
+         break;
+      case SCTP_I_ABANDONED:
+         if (cb)
+            cb->msgAbandonedArrived(assocId);
+         break;
+      case SCTP_I_SHUTDOWN_RECEIVED:
+         sctpEV3<<"SCTP_I_SHUTDOWN_RECEIVED\n";
+      if (cb)
+            cb->shutdownReceivedArrived(assocId);
+         break;
+      case SCTP_I_SENDQUEUE_FULL:
+         if (cb)
+            cb->sendqueueFullArrived(assocId);
+         break;
+      case SCTP_I_SENDQUEUE_ABATED:
+      {
+         SCTPCommand *cmd = check_and_cast<SCTPCommand *>(msg->removeControlInfo());
+         if (cb)
+         {
+            cb->sendqueueAbatedArrived(assocId, cmd->getNumMsgs());
+         }
+         delete cmd;
+         break;
+      }
+      case SCTP_I_RCV_STREAMS_RESETTED:
+      case SCTP_I_SEND_STREAMS_RESETTED:
+      case SCTP_I_RESET_REQUEST_FAILED: break;
+      case SCTP_I_ADDRESS_ADDED:
+      {
+         SCTPCommand* cmd=check_and_cast<SCTPCommand*>(msg->removeControlInfo());
+         if (cb)
+            cb->addressAddedArrived(assocId, cmd->getLocalAddr(),remoteAddr);
+         delete cmd;
+         break;
+      }
+      default:
+         opp_error("SCTPSocket: invalid msg kind %d, one of the SCTP_I_xxx constants expected", msg->getKind());
+   }
 
+   delete msg;
+}
 
+void SCTPSocket::setStreamPriority(uint32 stream, uint32 priority)
+{
+   cPacket *msg = new cPacket("SET_STREAM_PRIO", SCTP_C_SET_STREAM_PRIO);
+   SCTPSendCommand *cmd = new SCTPSendCommand();
+   cmd->setAssocId(assocId);
+   cmd->setSid(stream);
+   cmd->setPpid(priority);
+   msg->setControlInfo(cmd);
+   sendToSCTP(msg);
+}
diff --git a/src/transport/contract/SCTPSocket.h b/src/transport/contract/SCTPSocket.h
index 1f93086..5885fb9 100644
--- a/src/transport/contract/SCTPSocket.h
+++ b/src/transport/contract/SCTPSocket.h
@@ -30,8 +30,6 @@ class SCTPStatusInfo;
 class SCTP;
 
 
-
-
 class  INET_API SCTPSocket
 {
   public:
@@ -48,17 +46,18 @@ class  INET_API SCTPSocket
       public:
         virtual ~CallbackInterface() {}
         virtual void socketDataArrived(int assocId, void *yourPtr, cPacket *msg, bool urgent) = 0;
-    virtual void socketDataNotificationArrived(int assocId, void *yourPtr, cPacket *msg) = 0;
+        virtual void socketDataNotificationArrived(int assocId, void *yourPtr, cPacket *msg) = 0;
         virtual void socketEstablished(int assocId, void *yourPtr, uint64 buffer) {}
         virtual void socketPeerClosed(int assocId, void *yourPtr) {}
         virtual void socketClosed(int assocId, void *yourPtr) {}
         virtual void socketFailure(int assocId, void *yourPtr, int code) {}
         virtual void socketStatusArrived(int assocId, void *yourPtr, SCTPStatusInfo *status){}// {delete status;}
-    virtual void sendRequestArrived() {}
-    virtual void shutdownReceivedArrived(int connId) {}
-    virtual void sendqueueFullArrived(int connId) {}
-    virtual void sendqueueAbatedArrived(int connId, uint64 buffer) {}
-    virtual void addressAddedArrived(int assocId, IPvXAddress localAddr, IPvXAddress remoteAddr) {}
+        virtual void sendRequestArrived() {}
+        virtual void msgAbandonedArrived(int assocId) {}
+        virtual void shutdownReceivedArrived(int connId) {}
+        virtual void sendqueueFullArrived(int connId) {}
+        virtual void sendqueueAbatedArrived(int connId, uint64 buffer) {}
+        virtual void addressAddedArrived(int assocId, IPvXAddress localAddr, IPvXAddress remoteAddr) {}
     };
 
     enum State {NOT_BOUND, CLOSED, LISTENING, CONNECTING, CONNECTED, PEER_CLOSED, LOCALLY_CLOSED, SOCKERROR};
@@ -151,7 +150,8 @@ class  INET_API SCTPSocket
     void setInboundStreams(int streams) {inboundStreams = streams;};
     int getOutboundStreams() {return outboundStreams;};
     int getLastStream() {return lastStream;};
- /**
+    void setStreamPriority(uint32 stream, uint32 priority);
+    /**
      * Bind the socket to a local port number.
      */
     void bind(int localPort);
@@ -160,7 +160,7 @@ class  INET_API SCTPSocket
      * Bind the socket to a local port number and IP address (useful with
      * multi-homing).
      */
-   void bind(IPvXAddress localAddr, int localPort);
+    void bind(IPvXAddress localAddr, int localPort);
 
     void bindx(AddressVector localAddr, int localPort);
 
@@ -179,19 +179,23 @@ class  INET_API SCTPSocket
      * connection will be accepted, and SCTP will refuse subsequent ones.
      * See SCTPOpenCommand documentation (neddoc) for more info.
      */
-     void listen(bool fork=false, uint32 requests=0, uint32 messagesToPush=0);
+    void listen(bool fork=false, bool streamReset=false, uint32 requests=0, uint32 messagesToPush=0);
+
     /**
      * Active OPEN to the given remote socket.
      */
-    void connect(IPvXAddress remoteAddress, int32 remotePort, uint32 numRequests);
+    void connect(IPvXAddress remoteAddress, int32 remotePort, bool streamReset, int32 prMethod, uint32 numRequests);
+    void connect(IPvXAddress remoteAddress, int32 remotePort);
+    void connectx(AddressVector remoteAddresses, int32 remotePort, bool streamReset=false, int32 prMethod=0, uint32 numRequests=0);
 
-     void connectx(AddressVector remoteAddresses, int32 remotePort, uint32 numRequests=0);
     /**
      * Sends data packet.
      */
     void send(cPacket *msg, bool last=true, bool primary=true);
+    void send(cPacket *msg, int prMethod, double prValue, bool last);
+    void send(cPacket *msg, int prMethod, double prValue, bool last, int32 streamId);
 
-      void sendNotification(cPacket *msg);
+    void sendNotification(cPacket *msg);
     void sendRequest(cPacket *msg);
     /**
      * Closes the local end of the connection. With SCTP, a CLOSE operation
@@ -279,5 +283,3 @@ class  INET_API SCTPSocket
 };
 
 #endif
-
-
diff --git a/src/transport/sctp/ChunkMap.cc b/src/transport/sctp/ChunkMap.cc
new file mode 100644
index 0000000..a33cc86
--- /dev/null
+++ b/src/transport/sctp/ChunkMap.cc
@@ -0,0 +1,155 @@
+//
+// Copyright (C) 2010-2012 Thomas Dreibholz
+//
+// 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
+// of the License, 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 this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+
+#include "ChunkMap.h"
+
+#include <iostream>
+
+
+// ###### Constructor #######################################################
+ChunkMap::ChunkMap(const unsigned int initialSize, const unsigned int maxSize)
+{
+   // ====== Set performance tuning parameters ==============================
+   MaxEntries           = maxSize / MAP_ENTRY_BITSIZE;
+   CumAckShiftThreshold = (initialSize  / MAP_ENTRY_BITSIZE) / 2;
+
+   // ====== Create TSN map =================================================
+   MapBaseTSN           = 0;
+   MapEntries           = initialSize / MAP_ENTRY_BITSIZE;
+   MapArray             = new MAP_ENTRY_TYPE[MapEntries];
+   assert(MapArray != NULL);
+   for(unsigned int i = 0;i < MapEntries;i++) {
+      MapArray[i] = 0;
+   }
+   TSNEntries = MapEntries * MAP_ENTRY_BITSIZE;
+   TSNArray   = new SCTPDataVariables*[TSNEntries];
+   assert(TSNArray != NULL);
+   for(unsigned int i = 0;i < TSNEntries;i++) {
+      TSNArray[i] = NULL;
+   }
+}
+
+
+// ###### Destructor ########################################################
+ChunkMap::~ChunkMap()
+{
+   delete [] MapArray;
+   MapArray = NULL;
+   delete [] TSNArray;
+   TSNArray = NULL;
+}
+
+
+// ###### Print map #########################################################
+void ChunkMap::dump()
+{
+   std::cout << "ChunkMap[baseTSN=" << MapBaseTSN << "]: ";
+   for(unsigned int i = 0; i < MapEntries * MAP_ENTRY_BYTESIZE; i++) {
+      if(isAcked(MapBaseTSN + i)) {
+         std::cout << "<" << MapBaseTSN + i << "> ";
+      }
+   }
+   std::cout << std::endl;
+}
+
+
+// ###### Grow mapping and TSN arrays to include given TSN ##################
+void ChunkMap::grow(const uint32 highestTSN)
+{
+   // ====== Calculate new array size =======================================
+   const unsigned int newRequiredNumberOfEntries = ((highestTSN - MapBaseTSN) + (MAP_ENTRY_BITSIZE - 1)) /
+                                                      MAP_ENTRY_BITSIZE;
+   if(newRequiredNumberOfEntries > MaxEntries) {
+      std::cerr << "ERROR: ChunkMap::grow() - Required " << newRequiredNumberOfEntries
+                << " entries, but hard limit is " << MaxEntries << "!" << std::endl;
+      abort();
+   }
+   unsigned int newMapEntries = 2 * MapEntries;
+   while(newMapEntries < newRequiredNumberOfEntries) {
+      newMapEntries *= 2;
+   }
+   newMapEntries = std::min(newMapEntries, MaxEntries);
+   const unsigned int newTSNEntries = newMapEntries * MAP_ENTRY_BITSIZE;
+   assert(newTSNEntries > TSNEntries);
+
+   // ====== Copy map entries into new array ================================
+   MAP_ENTRY_TYPE* newMapArray = new MAP_ENTRY_TYPE[newMapEntries];
+   assert(newMapArray != NULL);
+   unsigned int i;
+   for(i = 0; i < MapEntries; i++) {
+      newMapArray[i] = MapArray[i];
+   }
+   for(     ; i < newMapEntries; i++) {
+      newMapArray[i] = 0;
+   }
+   delete [] MapArray;
+   MapArray = newMapArray;
+
+   // ====== Copy TSN entries into new array ================================
+   SCTPDataVariables** newTSNArray = new SCTPDataVariables*[newTSNEntries];
+   assert(newTSNArray != NULL);
+   for(i = 0; i < TSNEntries; i++) {
+      newTSNArray[i] = TSNArray[i];
+   }
+   for(     ; i < newTSNEntries; i++) {
+      newTSNArray[i] = NULL;
+   }
+   delete [] TSNArray;
+   TSNArray = newTSNArray;
+
+   MapEntries = newMapEntries;
+   TSNEntries = newTSNEntries;
+}
+
+
+// ###### Shift mapping and TSN arrays to handle CumAck #####################
+void ChunkMap::shift(const uint32 tsnDifference)
+{
+   // ====== Shift mapping array ============================================
+   const unsigned int mapDifference = tsnDifference / MAP_ENTRY_BITSIZE;
+   memmove((void*)&MapArray[0], (void*)&MapArray[mapDifference],
+            (MapEntries - mapDifference) * MAP_ENTRY_BYTESIZE);
+   memset((void*)&MapArray[MapEntries - mapDifference], 0,
+            mapDifference * MAP_ENTRY_BYTESIZE);
+
+   // ====== Shift TSN array ================================================
+   memmove((void*)&TSNArray[0], (void*)&TSNArray[tsnDifference],
+            (TSNEntries - tsnDifference) * sizeof(SCTPDataVariables*));
+   memset((void*)&TSNArray[TSNEntries - tsnDifference], 0,
+            tsnDifference * sizeof(SCTPDataVariables*));
+}
+
+
+// ###### CumAck TSN ########################################################
+void ChunkMap::cumAck(const uint32 cumAckTSN)
+{
+   const uint32 newMapBaseTSN = cumAckTSN - (uint32)GET_BIT(cumAckTSN);
+   const unsigned int tsnDifference = newMapBaseTSN - MapBaseTSN;
+
+   // ====== Move array, if appropriate =====================================
+   if(tsnDifference >= CumAckShiftThreshold) {
+      shift(tsnDifference);
+      MapBaseTSN = newMapBaseTSN;
+   }
+
+   // ====== CumAck TSNs which are still in the array =======================
+   for(uint32 tsn = MapBaseTSN; tsn <= cumAckTSN; tsn++) {
+      ack(tsn);
+      TSNArray[tsn - MapBaseTSN] = NULL;
+   }
+}
diff --git a/src/transport/sctp/ChunkMap.h b/src/transport/sctp/ChunkMap.h
new file mode 100644
index 0000000..39c2fc8
--- /dev/null
+++ b/src/transport/sctp/ChunkMap.h
@@ -0,0 +1,118 @@
+//
+// Copyright (C) 2010-2012 Thomas Dreibholz
+//
+// 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
+// of the License, 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 this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+
+#ifndef CHUNKMAP_H
+#define CHUNKMAP_H
+
+#include <omnetpp.h>
+#include <assert.h>
+
+
+struct SCTPDataVariables;
+
+
+// ====== Entry type definition  ============================================
+// ~~~~~~ 64-bit entry type ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// #define MAP_ENTRY_TYPE       unsigned long long
+// ~~~~~~ 32-bit entry type ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#define MAP_ENTRY_TYPE       uint32
+// ~~~~~~ 8-bit entry type ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// #define MAP_ENTRY_TYPE       unsigned char
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#define MAP_ENTRY_BYTESIZE   (unsigned int)sizeof(MAP_ENTRY_TYPE)
+#define MAP_ENTRY_BITSIZE    (8 * MAP_ENTRY_BYTESIZE)
+
+
+// ====== Helper functions to map relative TSN to entry and bit numbers =====
+#define GET_ENTRY(relTSN) ((unsigned int)((uint32)relTSN / (uint32)MAP_ENTRY_BITSIZE))
+#define GET_BIT(relTSN)   ((unsigned int)((uint32)relTSN & ((uint32)MAP_ENTRY_BITSIZE - 1)))
+
+
+class ChunkMap
+{
+   public:
+   ChunkMap(const unsigned int initialSize, const unsigned int maxSize);
+   ~ChunkMap();
+
+   void dump();
+   void cumAck(const uint32 cumAckTSN);
+
+   inline void ack(const uint32 tsn)   { ackOrUnack(tsn, true);  }
+   inline void unack(const uint32 tsn) { ackOrUnack(tsn, false); }
+
+   inline SCTPDataVariables* getChunk(const uint32 tsn) const {
+      const unsigned int relTSN = (tsn - MapBaseTSN);
+      assert(tsn >= MapBaseTSN);
+      assert(relTSN < TSNEntries);
+      return(TSNArray[relTSN]);
+   }
+
+   inline void setChunk(const uint32 tsn, SCTPDataVariables* chunk) {
+      const unsigned int relTSN = (tsn - MapBaseTSN);
+      assert(tsn >= MapBaseTSN);
+      if(relTSN >= TSNEntries) {
+         grow(tsn);
+      }
+      assert(relTSN < TSNEntries);
+      TSNArray[relTSN] = chunk;
+   }
+
+   inline void ackOrUnack(const uint32 tsn, const bool ack) {
+      const unsigned int relTSN = (tsn - MapBaseTSN);
+      const unsigned int entry  = GET_ENTRY(relTSN);
+      const unsigned int bit    = GET_BIT(relTSN);
+      assert(tsn >= MapBaseTSN);
+      if(entry >= MapEntries) {
+         grow(tsn - MapBaseTSN);
+         assert(entry < MapEntries);
+      }
+      if(ack) {
+         MapArray[entry] |= ((MAP_ENTRY_TYPE)1 << bit);
+      }
+      else {
+         MapArray[entry] &= ~((MAP_ENTRY_TYPE)1 << bit);
+      }
+   }
+
+   inline bool isAcked(const uint32 tsn) const {
+      const unsigned int relTSN = (tsn - MapBaseTSN);
+      const unsigned int entry  = GET_ENTRY(relTSN);
+      const unsigned int bit    = GET_BIT(relTSN);
+      assert(tsn >= MapBaseTSN);
+      assert(entry < MapEntries);
+      return(MapArray[entry] & ((MAP_ENTRY_TYPE)1 << bit));
+   }
+
+
+   // ====== Private data ===================================================
+   private:
+   void grow(const uint32 highestTSN);
+   void shift(const uint32 tsnDifference);
+
+   unsigned int        MapEntries;
+   uint32              MapBaseTSN;
+   MAP_ENTRY_TYPE*     MapArray;
+
+   unsigned int        TSNEntries;
+   SCTPDataVariables** TSNArray;
+
+   unsigned int        MaxEntries;
+   unsigned int        CumAckShiftThreshold;
+};
+
+#endif
diff --git a/src/transport/sctp/GapList.cc b/src/transport/sctp/GapList.cc
new file mode 100644
index 0000000..f7c7b08
--- /dev/null
+++ b/src/transport/sctp/GapList.cc
@@ -0,0 +1,466 @@
+//
+// Copyright (C) 2010-2012 Thomas Dreibholz
+//
+// 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
+// of the License, 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 this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+
+#include "GapList.h"
+
+
+// #define DEBUG_GAPLIST
+
+#ifdef DEBUG_GAPLIST
+#define sctpEV3 std::cout
+#warning DEBUG_GAPLIST is ON!
+#endif
+
+
+// ###### Constructor #######################################################
+SimpleGapList::SimpleGapList()
+{
+   NumGaps = 0;
+   for (unsigned int i = 0; i < MAX_GAP_COUNT; i++) {
+      GapStartList[i] = 0xffff00ff;
+      GapStopList[i]  = 0xffff0000;
+   }
+}
+
+
+// ###### Destructor ########################################################
+SimpleGapList::~SimpleGapList()
+{
+}
+
+
+// ###### Check gap list ####################################################
+void SimpleGapList::check(const uint32 cTsnAck) const
+{
+   for(uint32 i = 0; i < NumGaps; i++) {
+      if(i == 0) {
+         assert(tsnGt(GapStartList[i], cTsnAck + 1));
+      }
+      else {
+         assert(tsnGt(GapStartList[i], GapStopList[i - 1] + 1));
+      }
+      assert(tsnLe(GapStartList[i], GapStopList[i]));
+   }
+}
+
+
+// ###### Print gap list ####################################################
+void SimpleGapList::print(std::ostream& os) const
+{
+   os << "{";
+   for (uint32 i = 0; i < NumGaps; i++) {
+      if(i > 0) {
+         os << ",";
+      }
+      os << " " << GapStartList[i] << "-" << GapStopList[i];
+   }
+   os << " }";
+}
+
+
+// ###### Is TSN in gap list? ###############################################
+bool SimpleGapList::tsnInGapList(const uint32 tsn) const
+{
+    for (uint32 i = 0; i < NumGaps; i++) {
+      if (tsnBetween(GapStartList[i], tsn, GapStopList[i])) {
+         return true;
+      }
+   }
+   return false;
+}
+
+
+// ###### Forward CumAckTSN #################################################
+void SimpleGapList::forwardCumAckTSN(const uint32 cTsnAck)
+{
+   if (NumGaps > 0) {
+      // It is only possible to advance CumAckTSN when there are gaps.
+      uint32 counter = 0;
+      uint32 advance = 0;
+      while(counter < NumGaps) {
+         // Check whether CumAckTSN can be advanced.
+         if (tsnGe(cTsnAck, GapStartList[counter])) {   // Yes!
+            advance++;
+         }
+         else {   // No -> end of search.
+            break;
+         }
+         counter++;
+      }
+
+      if(advance > 0) {
+         // We can remove "advance" block now.
+         for (uint32 i = advance; i < NumGaps; i++) {
+            GapStartList[i - advance] = GapStartList[i];
+            GapStopList[i - advance]  = GapStopList[i];
+         }
+         NumGaps -= advance;
+      }
+   }
+}
+
+
+// ###### Try to advance CumAckTSN ##########################################
+bool SimpleGapList::tryToAdvanceCumAckTSN(uint32& cTsnAck)
+{
+   bool progress = false;
+   if (NumGaps > 0) {
+      // It is only possible to advance CumAckTSN when there are gaps.
+      uint32 counter = 0;
+      while(counter < NumGaps) {
+         // Check whether CumAckTSN can be advanced.
+         if (cTsnAck + 1 == GapStartList[0]) {   // Yes!
+            cTsnAck = GapStopList[0];
+            // We can take out all fragments of this block
+            for (uint32 i = 1; i < NumGaps; i++) {
+               GapStartList[i-1] = GapStartList[i];
+               GapStopList[i-1]  = GapStopList[i];
+            }
+            progress = true;
+         }
+         counter++;
+      }
+   }
+   return(progress);
+}
+
+
+// ###### Remove TSN from gap list ##########################################
+void SimpleGapList::removeFromGapList(const uint32 removedTSN)
+{
+   const int32 initialNumGaps = NumGaps;
+
+   for (int32 i = initialNumGaps - 1; i >= 0; i--) {
+      if (tsnBetween(GapStartList[i], removedTSN, GapStopList[i])) {
+         // ====== Gap block contains more than one TSN =====================
+         const int32 gapsize = (int32)(GapStopList[i] - GapStartList[i] + 1);
+         if (gapsize > 1) {
+            if (GapStopList[i] == removedTSN) {   // Remove stop TSN
+               GapStopList[i]--;
+            }
+            else if (GapStartList[i] == removedTSN) {   // Remove start TSN
+               GapStartList[i]++;
+            }
+            else {   // Block has to be splitted up
+               NumGaps = std::min(NumGaps + 1, (uint32)MAX_GAP_COUNT);   // T.D. 18.12.09: Enforce upper limit!
+               for (int32 j = NumGaps - 1;  j > i; j--) {
+                  GapStopList[j]  = GapStopList[j - 1];
+                  GapStartList[j] = GapStartList[j - 1];
+               }
+               GapStopList[i]      = removedTSN - 1;
+               if((uint32)i + 1 < NumGaps) {
+                  GapStartList[i + 1] = removedTSN + 1;
+               }
+            }
+         }
+
+         // ====== Just a single TSN in the gap block (start==stop) =========
+         else {
+            for (int32 j = i; j <= initialNumGaps - 1; j++) {
+               GapStopList[j]  = GapStopList[j + 1];
+               GapStartList[j] = GapStartList[j + 1];
+            }
+            GapStartList[initialNumGaps - 1] = 0;
+            GapStopList[initialNumGaps - 1]  = 0;
+            NumGaps--;
+         }
+
+         break;  // TSN removed -> done!
+      }
+   }
+}
+
+
+// ###### Add TSN to gap list ###############################################
+bool SimpleGapList::updateGapList(const uint32 receivedTSN,
+                                  uint32&      cTsnAck,
+                                  bool&        newChunkReceived)
+{
+   if(tsnLe(receivedTSN, cTsnAck)) {
+      // Received TSN covered by CumAckTSN -> nothing to do.
+      return(false);
+   }
+
+   uint32 lo = cTsnAck + 1;
+   for (uint32 i = 0; i < NumGaps; i++) {
+      if (GapStartList[i] > 0) {
+         const uint32 hi = GapStartList[i] - 1;
+         if (tsnBetween(lo, receivedTSN, hi)) {
+            const uint32 gapsize = hi - lo + 1;
+            if (gapsize > 1) {
+               /**
+               * TSN either sits at the end of one gap, and thus changes gap
+               * boundaries, or it is in between two gaps, and becomes a new gap
+               */
+               if (receivedTSN == hi) {
+                  GapStartList[i] = receivedTSN;
+                  newChunkReceived = true;
+                  return true;
+               }
+               else if (receivedTSN == lo) {
+                  if (receivedTSN == (cTsnAck + 1)) {
+                     cTsnAck++;
+                     newChunkReceived = true;
+                     return true;
+                  }
+                  /* some gap must increase its upper bound */
+                  GapStopList[i-1] = receivedTSN;
+                  newChunkReceived = true;
+                  return true;
+               }
+               else {   /* a gap in between */
+                  NumGaps = std::min(NumGaps + 1, (uint32)MAX_GAP_COUNT);   // T.D. 18.12.09: Enforce upper limit!
+
+                  for (uint32 j = NumGaps - 1; j > i; j--) {   // T.D. 18.12.09: Fixed invalid start value.
+                     GapStartList[j] = GapStartList[j-1];
+                     GapStopList[j] = GapStopList[j-1];
+                  }
+                  GapStartList[i]=receivedTSN;
+                  GapStopList[i]=receivedTSN;
+                  newChunkReceived = true;
+                  return true;
+               }
+            }
+            else {   /* alright: gapsize is 1: our received tsn may close gap between fragments */
+               if (lo == cTsnAck + 1) {
+                  cTsnAck = GapStopList[i];
+                  if (i == NumGaps-1) {
+                     GapStartList[i] = 0;
+                     GapStopList[i] = 0;
+                  }
+                  else {
+                     for (uint32 j = i; j < NumGaps - 1; j++) {      // T.D. 18.12.09: Fixed invalid end value.
+                        GapStartList[j] = GapStartList[j + 1];
+                        GapStopList[j]  = GapStopList[j + 1];
+                     }
+                  }
+                  NumGaps--;
+                  newChunkReceived = true;
+                  return true;
+               }
+               else {
+                  GapStopList[i-1] = GapStopList[i];
+                  if (i == NumGaps-1) {
+                     GapStartList[i] = 0;
+                     GapStopList[i] = 0;
+                  }
+                  else {
+                     for (uint32 j = i; j < NumGaps - 1; j++) {      // T.D. 18.12.09: Fixed invalid end value.
+                        GapStartList[j] = GapStartList[j + 1];
+                        GapStopList[j]  = GapStopList[j + 1];
+                     }
+                  }
+                  NumGaps--;
+                  newChunkReceived = true;
+                  return true;
+               }
+            }
+         }
+         else {  /* receivedTSN is not in the gap between these fragments... */
+            lo = GapStopList[i] + 1;
+         }
+      } /* end: for */
+   } /* end: for */
+
+   // ====== We have reached the end of the list ============================
+   if (receivedTSN == lo) {   // just increase CumAckTSN, handle further update of CumAckTSN later
+      if (receivedTSN == cTsnAck + 1) {
+         cTsnAck          = receivedTSN;
+         newChunkReceived = true;
+         return true;
+      }
+      // Update last fragment, stop TSN by one.
+      GapStopList[NumGaps-1]++;
+
+      newChunkReceived = true;
+      return true;
+
+   }
+   else {
+      // T.D. 24.09.2010:
+      // If the received TSN is new, the list is either empty or the received
+      // TSN is larger than the last gap end + 1. Otherwise, the TSN has already
+      // been acknowledged.
+      if( (NumGaps == 0) ||
+          (tsnGt(receivedTSN, GapStopList[NumGaps - 1] + 1)) ) {
+         // A new fragment altogether, past the end of the list
+         if(NumGaps < MAX_GAP_COUNT) {   // T.D. 18.12.09: Enforce upper limit!
+            GapStartList[NumGaps] = receivedTSN;
+            GapStopList[NumGaps]  = receivedTSN;
+            NumGaps++;
+            newChunkReceived = true;
+         }
+      }
+      return true;
+   }
+
+   return false;
+}
+
+
+
+// ###### Constructor #######################################################
+GapList::GapList()
+{
+   CumAckTSN = 0;
+}
+
+
+// ###### Destructor ########################################################
+GapList::~GapList()
+{
+}
+
+
+// ###### Check gap list ####################################################
+void GapList::check() const
+{
+   CombinedGapList.check(CumAckTSN);
+   RevokableGapList.check(CumAckTSN);
+   NonRevokableGapList.check(CumAckTSN);
+}
+
+
+// ###### Print gap list ####################################################
+void GapList::print(std::ostream& os) const
+{
+   os << "CumAck=" << CumAckTSN;
+   os << "   Combined-Gaps=";
+   CombinedGapList.print(os);
+   os << "   R-Gaps=";
+   RevokableGapList.print(os);
+   os << "   NR-Gaps=";
+   NonRevokableGapList.print(os);
+}
+
+
+// ###### Forward CumAckTSN #################################################
+void GapList::forwardCumAckTSN(const uint32 cumAckTSN)
+{
+   CumAckTSN = cumAckTSN;
+   CombinedGapList.forwardCumAckTSN(CumAckTSN);
+   RevokableGapList.forwardCumAckTSN(CumAckTSN);
+   NonRevokableGapList.forwardCumAckTSN(CumAckTSN);
+#ifdef DEBUG_GAPLIST
+   check();
+#endif
+}
+
+
+// ###### Advance CumAckTSN #################################################
+bool GapList::tryToAdvanceCumAckTSN()
+{
+   if(CombinedGapList.tryToAdvanceCumAckTSN(CumAckTSN)) {
+      RevokableGapList.forwardCumAckTSN(CumAckTSN);
+      NonRevokableGapList.forwardCumAckTSN(CumAckTSN);
+#ifdef DEBUG_GAPLIST
+      check();
+#endif
+      return(true);
+   }
+   return(false);
+}
+
+
+// ###### Remove TSN from gap list ##########################################
+void GapList::removeFromGapList(const uint32 removedTSN)
+{
+   RevokableGapList.removeFromGapList(removedTSN);
+   NonRevokableGapList.removeFromGapList(removedTSN);
+   CombinedGapList.removeFromGapList(removedTSN);
+#ifdef DEBUG_GAPLIST
+   check();
+#endif
+}
+
+
+// ###### Add TSN to gap list ###############################################
+bool GapList::updateGapList(const uint32 receivedTSN,
+                            bool&        newChunkReceived,
+                            bool         tsnIsRevokable)
+{
+   uint32 oldCumAckTSN = CumAckTSN;
+   if(tsnIsRevokable) {
+      // Once a TSN become non-revokable, it cannot become revokable again!
+      // However, if the list became too long, updateGapList() may be called
+      // again when the chunk is received again.
+      RevokableGapList.updateGapList(receivedTSN, oldCumAckTSN, newChunkReceived);
+   }
+   else {
+      if(NonRevokableGapList.updateGapList(receivedTSN, oldCumAckTSN, newChunkReceived) == true) {
+         // TSN has moved from revokable to non-revokable!
+         RevokableGapList.removeFromGapList(receivedTSN);
+      }
+   }
+
+   // Finally, add TSN to combined list and set CumAckTSN.
+   oldCumAckTSN = CumAckTSN;
+   const bool newChunk = CombinedGapList.updateGapList(receivedTSN, CumAckTSN, newChunkReceived);
+   if(oldCumAckTSN != CumAckTSN) {
+      RevokableGapList.forwardCumAckTSN(CumAckTSN);
+      NonRevokableGapList.forwardCumAckTSN(CumAckTSN);
+   }
+
+#ifdef DEBUG_GAPLIST
+   check();
+#endif
+   return(newChunk);
+}
+
+
+
+#if 0
+int main(int argc, char** argv)
+{
+   bool    newChunk = false;
+   GapList gl;
+
+   gl.setInitialCumAckTSN(1000);
+   gl.updateGapList(1004, newChunk); gl.check();
+   gl.updateGapList(1005, newChunk); gl.check();
+   std::cout << "A1:\t"; gl.print(std::cout); std::cout << endl;
+
+   gl.updateGapList(1009, newChunk); gl.check();
+   gl.updateGapList(1010, newChunk); gl.check();
+   std::cout << "A2:\t"; gl.print(std::cout); std::cout << endl;
+
+   gl.updateGapList(1006, newChunk, false); gl.check();
+   gl.updateGapList(1008, newChunk, false); gl.check();
+
+   std::cout << "A3:\t"; gl.print(std::cout); std::cout << endl;
+   gl.updateGapList(1007, newChunk); gl.check();
+
+   std::cout << "A3:\t"; gl.print(std::cout); std::cout << endl;
+   gl.updateGapList(1002, newChunk); gl.check();
+   std::cout << "A4:\t"; gl.print(std::cout); std::cout << endl;
+   gl.updateGapList(1001, newChunk); gl.check();
+   std::cout << "A5:\t"; gl.print(std::cout); std::cout << endl;
+   gl.updateGapList(1003, newChunk); gl.check();
+
+   std::cout << "A6:\t"; gl.print(std::cout); std::cout << endl;
+   gl.updateGapList(1004, newChunk, false); gl.check();   // Already-acked
+   gl.updateGapList(1005, newChunk, false); gl.check();   // Already-acked
+
+   std::cout << "A7:\t"; gl.print(std::cout); std::cout << endl;
+   gl.updateGapList(1012, newChunk, true);  gl.check();
+   std::cout << "A8:\t"; gl.print(std::cout); std::cout << endl;
+   gl.updateGapList(1012, newChunk, false); gl.check();   // Already-acked
+
+   std::cout << "Ende:\t"; gl.print(std::cout); std::cout << endl;
+}
+#endif
diff --git a/src/transport/sctp/GapList.h b/src/transport/sctp/GapList.h
new file mode 100644
index 0000000..99ee76f
--- /dev/null
+++ b/src/transport/sctp/GapList.h
@@ -0,0 +1,159 @@
+//
+// Copyright (C) 2010-2012 Thomas Dreibholz
+//
+// 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
+// of the License, 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 this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+
+#ifndef GAPLIST_H
+#define GAPLIST_H
+
+#include <omnetpp.h>
+#include <assert.h>
+
+#include "SCTPSeqNumbers.h"
+
+#define MAX_GAP_COUNT 500
+
+
+class SimpleGapList
+{
+   public:
+   SimpleGapList();
+   ~SimpleGapList();
+
+   void check(const uint32 cTsnAck) const;
+   void print(std::ostream& os) const;
+
+   inline uint32 getNumGaps() const {
+      return(NumGaps);
+   }
+   inline uint32 getGapStart(const uint32 index) const {
+      assert(index < NumGaps);
+      return(GapStartList[index]);
+   }
+   inline uint32 getGapStop(const uint32 index) const {
+      assert(index < NumGaps);
+      return(GapStopList[index]);
+   }
+
+   bool tsnInGapList(const uint32 tsn) const;
+   void forwardCumAckTSN(const uint32 cTsnAck);
+   bool tryToAdvanceCumAckTSN(uint32& cTsnAck);
+   void removeFromGapList(const uint32 removedTSN);
+   bool updateGapList(const uint32 receivedTSN,
+                      uint32&      cTsnAck,
+                      bool&        newChunkReceived);
+
+
+   // ====== Private data ===================================================
+   private:
+   uint32 NumGaps;
+   uint32 GapStartList[MAX_GAP_COUNT];
+   uint32 GapStopList[MAX_GAP_COUNT];
+};
+
+
+class GapList
+{
+   public:
+   GapList();
+   ~GapList();
+
+   inline void setInitialCumAckTSN(const uint32 cumAckTSN) {
+      assert(CombinedGapList.getNumGaps() == 0);
+      CumAckTSN = cumAckTSN;
+   }
+   inline uint32 getCumAckTSN() const {
+      return(CumAckTSN);
+   }
+   inline uint32 getHighestTSNReceived() const {
+      if (CombinedGapList.getNumGaps() > 0) {
+         return(CombinedGapList.getGapStop(CombinedGapList.getNumGaps() - 1));
+      }
+      else {
+         return(CumAckTSN);
+      }
+   }
+
+   enum GapType {
+      GT_Any          = 0,
+      GT_Revokable    = 1,
+      GT_NonRevokable = 2
+   };
+
+   inline uint32 getNumGaps(const GapType type) const {
+      if(type == GT_Revokable) {
+         return(RevokableGapList.getNumGaps());
+      }
+      else if(type == GT_NonRevokable) {
+         return(NonRevokableGapList.getNumGaps());
+      }
+      else {
+         return(CombinedGapList.getNumGaps());
+      }
+   }
+
+   inline bool tsnInGapList(const uint32 tsn) const {
+      return(CombinedGapList.tsnInGapList(tsn));
+   }
+   inline bool tsnIsRevokable(const uint32 tsn) const {
+      return(RevokableGapList.tsnInGapList(tsn));
+   }
+   inline bool tsnIsNonRevokable(const uint32 tsn) const {
+      return(NonRevokableGapList.tsnInGapList(tsn));
+   }
+
+   inline uint32 getGapStart(const GapType type, const uint32 index) const {
+      if(type == GT_Revokable) {
+         return(RevokableGapList.getGapStart(index));
+      }
+      else if(type == GT_NonRevokable) {
+         return(NonRevokableGapList.getGapStart(index));
+      }
+      else {
+         return(CombinedGapList.getGapStart(index));
+      }
+   }
+   inline uint32 getGapStop(const GapType type, const uint32 index) const {
+      if(type == GT_Revokable) {
+         return(RevokableGapList.getGapStop(index));
+      }
+      else if(type == GT_NonRevokable) {
+         return(NonRevokableGapList.getGapStop(index));
+      }
+      else {
+         return(CombinedGapList.getGapStop(index));
+      }
+   }
+
+   void check() const;
+   void print(std::ostream& os) const;
+
+   void forwardCumAckTSN(const uint32 cumAckTSN);
+   bool tryToAdvanceCumAckTSN();
+   void removeFromGapList(const uint32 removedTSN);
+   bool updateGapList(const uint32 receivedTSN,
+                      bool&        newChunkReceived,
+                      bool         tsnIsRevokable = true);
+
+   // ====== Private data ===================================================
+   private:
+   uint32        CumAckTSN;
+   SimpleGapList RevokableGapList;
+   SimpleGapList NonRevokableGapList;
+   SimpleGapList CombinedGapList;
+};
+
+#endif
diff --git a/src/transport/sctp/KNOWN_BUGS b/src/transport/sctp/KNOWN_BUGS
new file mode 100644
index 0000000..1d2ed89
--- /dev/null
+++ b/src/transport/sctp/KNOWN_BUGS
@@ -0,0 +1,6 @@
+Sequence Number wrapping will not work properly, since in many places of
+SCTPAssociationRcvMessage.cc the sequence numbers are simply handled as
+uint32 in "for" loops, "if" clauses, etc.
+
+Idea:
+Replace uint32 type by class which properly handles wraps.
diff --git a/src/transport/sctp/QoSStatsCollector.cc b/src/transport/sctp/QoSStatsCollector.cc
new file mode 100644
index 0000000..392e3f1
--- /dev/null
+++ b/src/transport/sctp/QoSStatsCollector.cc
@@ -0,0 +1,269 @@
+//
+// Copyright (C) 2010-2012 Thomas Dreibholz
+//
+// 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
+// of the License, 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 this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+
+#include "QoSStatsCollector.h"
+
+#include <assert.h>
+#include <iostream>
+
+
+// ###### Constructor #######################################################
+QoSStats::QoSStats(cModule*                             module,
+                   const std::string&                   name,
+                   const enum QoSStats::QoSStatsVariant variant)
+{
+   Name             = name;
+   Variant          = variant;
+   TrackingInterval = 1.0;
+   ValueSum         = 0;
+   cOutVector::setName(name.c_str());
+   cOutVector::setUnit("s");
+}
+
+
+// ###### Destructor ########################################################
+QoSStats::~QoSStats()
+{
+   QoSStatsEntryType::iterator first = QoSStatsSet.begin();
+   while(first != QoSStatsSet.end()) {
+      delete *first;
+      QoSStatsSet.erase(first);
+      first = QoSStatsSet.begin();
+   }
+}
+
+
+// ###### Purge out-of-date values ##########################################
+void QoSStats::purge(const simtime_t& now)
+{
+   QoSStatsEntryType::iterator first = QoSStatsSet.begin();
+   while(first != QoSStatsSet.end()) {
+
+      const QoSStatsEntry* statEntry = *first;
+      if(now - statEntry->TimeStamp <= TrackingInterval) {
+         break;
+      }
+      assert(ValueSum >=  statEntry->Value);
+      ValueSum -= statEntry->Value;
+      delete statEntry;
+      QoSStatsSet.erase(first);
+
+      first = QoSStatsSet.begin();
+   }
+}
+
+
+// ###### Dump storage ######################################################
+void QoSStats::dump(std::ostream& os) const
+{
+   unsigned long long valueSum = 0;
+   os << Name << " (ValueSum=" << ValueSum << "):" << endl;
+   for(QoSStatsEntryType::iterator iterator = QoSStatsSet.begin();
+       iterator != QoSStatsSet.end(); iterator++) {
+      const QoSStatsEntry* statEntry = *iterator;
+      valueSum += statEntry->Value;
+      os << " - " << statEntry->TimeStamp << "\t" << statEntry->Value << endl;
+   }
+   assert(ValueSum == valueSum);
+}
+
+// ###### Add value #########################################################
+void QoSStats::add(const simtime_t& now, const unsigned int value)
+{
+   // ====== Get rid of out-of-date entries =================================
+   purge(now);
+
+   // ====== Add new entry ==================================================
+   QoSStatsEntry* statEntry = new QoSStatsEntry;
+   statEntry->TimeStamp = now;
+   statEntry->Value     = value;
+   QoSStatsSet.insert(statEntry);
+   ValueSum += statEntry->Value;
+
+   // ====== Record rate or mean ============================================
+   double result;
+   switch(Variant) {
+      case QSV_Rate:
+         result = getRate();
+       break;
+      case QSV_Mean:
+         result = getMean();
+       break;
+      default:
+         abort();
+       break;
+   }
+   record(result);
+}
+
+
+// ###### Constructor #######################################################
+QoSStatsCollector::QoSStatsCollector()
+{
+   HasBeenDumped        = false;
+   IsActive             = false;
+   TrackingInterval     = 1.0;
+
+   EnqueuingStats       = NULL;
+   DequeuingStats       = NULL;
+   TransmissionStats    = NULL;
+   AcknowledgementStats = NULL;
+   DropStats            = NULL;
+}
+
+
+// ###### Destructor ########################################################
+QoSStatsCollector::~QoSStatsCollector()
+{
+   if(!HasBeenDumped) {
+      dumpScalars();
+   }
+   clear();
+}
+
+
+// ###### Activate QoS tracking #############################################
+void QoSStatsCollector::activate(const simtime_t& trackingInterval)
+{
+   IsActive = true;
+   if(trackingInterval > 0.0) {
+      TrackingInterval = trackingInterval;
+   }
+}
+
+
+// ###### Deactivate QoS tracking ###########################################
+void QoSStatsCollector::deactivate()
+{
+   IsActive = false;
+}
+
+
+// ###### Record action #####################################################
+void QoSStatsCollector::recordEnqueuing(const uint32     length,
+                                        const simtime_t& enqueuingTime)
+{
+   if((IsActive) && (EnqueuingStats)) {
+      EnqueuingStats->add(enqueuingTime, length);
+   }
+}
+
+
+// ###### Record action ######################################################
+void QoSStatsCollector::recordTransmission(const uint32     tsn,
+                                           const uint32     length,
+                                           const simtime_t& transmissionTime)
+{
+   if((IsActive) && (TransmissionStats)) {
+      TransmissionStats->add(transmissionTime, length);
+   }
+}
+
+
+// ###### Record action ######################################################
+void QoSStatsCollector::recordAcknowledgement(const uint32     tsn,
+                                              const uint32     length,
+                                              const simtime_t& transmissionTime,
+                                              const simtime_t& acknowledgementTime)
+{
+   if(IsActive) {
+      if(AcknowledgementStats) {
+         AcknowledgementStats->add(acknowledgementTime, length);
+      }
+      if(TxToAckStats) {
+         const unsigned int txToAckTime =
+            (unsigned int)rint(1000000.0 * (acknowledgementTime - transmissionTime).dbl());
+         TxToAckStats->add(acknowledgementTime, txToAckTime);
+      }
+   }
+}
+
+
+// ###### Record action ######################################################
+void QoSStatsCollector::recordDequeuing(const uint32     length,
+                                        const simtime_t& enqueuingTime,
+                                        const simtime_t& dequeuingTime)
+{
+   if((IsActive) && (DequeuingStats)) {
+      DequeuingStats->add(dequeuingTime, length);
+   }
+}
+
+
+// ###### Record action ######################################################
+void QoSStatsCollector::recordDrop(const uint32     tsn,
+                                   const uint32     length,
+                                   const simtime_t& transmissionTime,
+                                   const simtime_t& dropTime)
+{
+   if(IsActive) {
+      // ...
+   }
+}
+
+
+// ###### Initialize ########################################################
+void QoSStatsCollector::initialize(cModule* module, const char* name)
+{
+   HasBeenInitialized   = false;
+   Parent               = module;
+   Name                 = std::string((name != NULL) ? name : "");
+
+   EnqueuingStats       = new QoSStats(module, "Enqueuing Statistics " + Name);
+   DequeuingStats       = new QoSStats(module, "Dequeuing Statistics " + Name);
+   TransmissionStats    = new QoSStats(module, "Transmission Statistics " + Name);
+   AcknowledgementStats = new QoSStats(module, "Acknowledgement Statistics " + Name);
+   TxToAckStats         = new QoSStats(module, "TxToAck Statistics " + Name, QoSStats::QSV_Mean);
+   DropStats            = new QoSStats(module, "Drop Statistics " + Name);
+}
+
+
+// ###### Clear all statistics ##############################################
+void QoSStatsCollector::clear()
+{
+   if(EnqueuingStats) {
+      delete EnqueuingStats;
+   }
+   if(DequeuingStats) {
+      delete DequeuingStats;
+   }
+   if(TransmissionStats) {
+      delete TransmissionStats;
+   }
+   if(AcknowledgementStats) {
+      delete AcknowledgementStats;
+   }
+   if(TxToAckStats) {
+      delete TxToAckStats;
+   }
+   if(DropStats) {
+      delete DropStats;
+   }
+   EnqueuingStats       = NULL;
+   DequeuingStats       = NULL;
+   TransmissionStats    = NULL;
+   AcknowledgementStats = NULL;
+   DropStats            = NULL;
+}
+
+
+// ###### Dump statistics scalars ###########################################
+void QoSStatsCollector::dumpScalars()
+{
+   HasBeenDumped = true;
+}
diff --git a/src/transport/sctp/QoSStatsCollector.h b/src/transport/sctp/QoSStatsCollector.h
new file mode 100644
index 0000000..2df1058
--- /dev/null
+++ b/src/transport/sctp/QoSStatsCollector.h
@@ -0,0 +1,165 @@
+//
+// Copyright (C) 2010-2012 Thomas Dreibholz
+//
+// 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
+// of the License, 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 this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+
+#ifndef QOSSTATSCOLLECTOR_H
+#define QOSSTATSCOLLECTOR_H
+
+#include <iostream>
+#include <omnetpp.h>
+
+
+class QoSStats : public cOutVector
+{
+   // ====== Public methods =================================================
+   public:
+   enum QoSStatsVariant {
+      QSV_Rate = 1,
+      QSV_Mean = 2
+   };
+
+   QoSStats(cModule*                   module,
+            const std::string&         name,
+            const enum QoSStatsVariant variant = QSV_Rate);
+   virtual ~QoSStats();
+
+   inline simtime_t getTrackingInterval() const {
+      return(TrackingInterval);
+   }
+   inline void setTrackingInterval(const simtime_t& trackingInterval) {
+      TrackingInterval = trackingInterval;
+   }
+
+   inline double getRate() const {
+      const QoSStatsEntryType::iterator first = QoSStatsSet.begin();
+      QoSStatsEntryType::iterator       last  = QoSStatsSet.end();
+      if(first != last) {
+         last--;   // There must be at least one element ...
+         if(first != last) {   // There are at least two elements ...
+            return(ValueSum / (double)((*last)->TimeStamp - (*first)->TimeStamp).dbl());
+         }
+      }
+      return(0.0);
+   }
+   inline double getMean() const {
+      const QoSStatsEntryType::iterator first = QoSStatsSet.begin();
+      QoSStatsEntryType::iterator       last  = QoSStatsSet.end();
+      if(first != last) {
+         last--;   // There must be at least one element ...
+         if(first != last) {   // There are at least two elements ...
+            return(ValueSum / (double)QoSStatsSet.size());
+         }
+      }
+      return(0.0);
+   }
+
+   void add(const simtime_t& now, const unsigned int value);
+   void dump(std::ostream& os) const;
+
+   // ====== Private data ===================================================
+   private:
+   struct QoSStatsEntry {
+      simtime_t    TimeStamp;
+      unsigned int Value;
+   };
+   struct QoSStatsEntryLess
+   {
+      inline bool operator()(const QoSStatsEntry* a, const QoSStatsEntry* b) const {
+         return(a->TimeStamp <= b->TimeStamp);
+      }
+   };
+   typedef std::set<QoSStatsEntry*, QoSStatsEntryLess> QoSStatsEntryType;
+
+   void purge(const simtime_t& now);
+
+   simtime_t            TrackingInterval;
+   unsigned long long   ValueSum;
+   QoSStatsEntryType    QoSStatsSet;
+   std::string          Name;
+   enum QoSStatsVariant Variant;
+};
+
+
+/*
+   Actions to record:
+   1. Chunk Enqueuing       (tsn, enqueuingTime, length)
+   2. Chunk Dequeuing       (tsn, enqueuingTime, dequeuingTime, length)
+   3. Chunk Transmission    (tsn, transmissionTime, length)
+   4. Chunk Acknowledgement (tsn, transmissionTime, ackTime, length)
+   5. Chunk Drop            (tsn, transmissionTime, dropTime, length)
+
+   Stream QoS:
+   Enqueuing Rate, Dequeuing Rate
+
+   Path QoS:
+   Enqueuing Rate ("scheduling rate"), Dequeuing Rate ("completion rate")
+   Transmission Rate, Acknowledgement Rate, Drop Rate
+*/
+
+class QoSStatsCollector
+{
+   public:
+   QoSStatsCollector();
+   ~QoSStatsCollector();
+
+   virtual void initialize(cModule* module = NULL, const char* name = NULL);
+   virtual void clear();
+   virtual void dumpScalars();
+
+   inline bool isActive() const {
+      return(IsActive);
+   }
+   void activate(const simtime_t& trackingInterval = -1.0);
+   void deactivate();
+
+   void recordEnqueuing(const uint32     length,
+                        const simtime_t& enqueuingTime);
+   void recordTransmission(const uint32     tsn,
+                           const uint32     length,
+                           const simtime_t& transmissionTime);
+   void recordAcknowledgement(const uint32     tsn,
+                              const uint32     length,
+                              const simtime_t& transmissionTime,
+                              const simtime_t& acknowledgementTime);
+   void recordDequeuing(const uint32     length,
+                        const simtime_t& enqueuingTime,
+                        const simtime_t& dequeuingTime);
+   void recordDrop(const uint32     tsn,
+                   const uint32     length,
+                   const simtime_t& transmissionTime,
+                   const simtime_t& dropTime);
+
+   // ====== Public data ====================================================
+   public:
+   QoSStats*   EnqueuingStats;           // Enqueuing (rate -> bytes/s)
+   QoSStats*   DequeuingStats;           // Dequeuing (rate -> bytes/s)
+   QoSStats*   TransmissionStats;        // Data Transmission (rate -> bytes/s)
+   QoSStats*   AcknowledgementStats;     // Data Acknowledgement (rate -> bytes/s)
+   QoSStats*   TxToAckStats;             // Delay from transmission to acknowledgement (mean -> microseconds)
+   QoSStats*   DropStats;
+
+   // ====== Private data ===================================================
+   private:
+   cModule*    Parent;
+   std::string Name;
+   simtime_t   TrackingInterval;
+   bool        IsActive;
+   bool        HasBeenInitialized;
+   bool        HasBeenDumped;
+};
+
+#endif
diff --git a/src/transport/sctp/SCTP.cc b/src/transport/sctp/SCTP.cc
index 74d917d..2b1fa11 100644
--- a/src/transport/sctp/SCTP.cc
+++ b/src/transport/sctp/SCTP.cc
@@ -1,6 +1,6 @@
 //
-// Copyright (C) 2005-2010 Irene Ruengeler
-// Copyright (C) 2009-2010 Thomas Dreibholz
+// Copyright (C) 2008 Irene Ruengeler
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,11 +9,12 @@
 //
 // 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
+// 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 this program; if not, see <http://www.gnu.org/licenses/>.
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 //
 
 
@@ -28,865 +29,884 @@
 Define_Module(SCTP);
 
 
-bool SCTP::testing;
-bool SCTP::logverbose;
+bool  SCTP::testing;
+bool  SCTP::logverbose;
+bool  SCTP::checkQueues;
+int32 SCTP::nextAssocId = 0;
 
-int32 SCTP::nextConnId = 0;
 
-
-void SCTP::printInfoConnMap()
+void SCTP::printInfoAssocMap()
 {
-    SCTPAssociation* assoc;
-    SockPair      key;
-    sctpEV3<<"Number of Assocs: "<<sizeConnMap<<"\n";
-    if (sizeConnMap>0)
-    {
-        for (SctpConnMap::iterator i = sctpConnMap.begin(); i!=sctpConnMap.end(); ++i)
-        {
-            assoc = i->second;
-            key = i->first;
-
-                sctpEV3<<"assocId: "<<assoc->assocId<<"  assoc: "<<assoc<<" src: "<<IPvXAddress(key.localAddr)<<" dst: "<<IPvXAddress(key.remoteAddr)<<" lPort: "<<key.localPort<<" rPort: "<<key.remotePort<<"\n";
-
-        }
-
-        sctpEV3<<"\n";
-    }
-
+   SCTPAssociation* assoc;
+   SockPair         key;
+   sctpEV3 << "Number of Assocs: " << sizeAssocMap << endl;
+   if (sizeAssocMap > 0) {
+      for (SctpAssocMap::iterator i = sctpAssocMap.begin(); i!=sctpAssocMap.end(); ++i) {
+         assoc = i->second;
+         key   = i->first;
+         sctpEV3 << "assocId: " << assoc->assocId << "  assoc: " << assoc
+                 << " src: "    << IPvXAddress(key.localAddr)
+                 << " dst: "    << IPvXAddress(key.remoteAddr)
+                 << " lPort: "  << key.localPort
+                 << " rPort: "  << key.remotePort << endl;
+      }
+      sctpEV3 << endl;
+   }
 }
 
 void SCTP::printVTagMap()
 {
-    int32 assocId;
-    VTagPair      key;
-    sctpEV3<<"Number of Assocs: "<<sctpVTagMap.size()<<"\n";
-    if (sctpVTagMap.size()>0)
-    {
-        for (SctpVTagMap::iterator i = sctpVTagMap.begin(); i!=sctpVTagMap.end(); ++i)
-        {
-            assocId = i->first;
-            key = i->second;
-
-                sctpEV3<<"assocId: "<<assocId<<" peerVTag: "<<key.peerVTag<<
-                " localVTag: "<<key.localVTag<<
-                " localPort: "<<key.localPort<<" rPort: "<<key.remotePort<<"\n";
-        }
-
-        sctpEV3<<"\n";
-    }
+   int32 assocId;
+   VTagPair   key;
+   sctpEV3 << "Number of Assocs: "<< sctpVTagMap.size() << endl;
+   if (sctpVTagMap.size()>0)
+   {
+      for (SctpVTagMap::iterator i = sctpVTagMap.begin(); i!=sctpVTagMap.end(); ++i)
+      {
+         assocId = i->first;
+         key = i->second;
+         sctpEV3 << "assocId: "    << assocId << " peerVTag: " << key.peerVTag
+                 << " localVTag: " << key.localVTag
+                 << " localPort: " << key.localPort
+                 << " rPort: "     << key.remotePort << endl;
+      }
+      sctpEV3 << endl;
+   }
 }
 
 void SCTP::bindPortForUDP()
 {
-    EV << "Binding to UDP port " << 9899 << endl;
-
-    cMessage *msg = new cMessage("UDP_C_BIND", UDP_C_BIND);
-    UDPControlInfo *ctrl = new UDPControlInfo();
-    ctrl->setSrcPort(9899);
-    ctrl->setSockId(UDPSocket::generateSocketId());
-    msg->setControlInfo(ctrl);
-    send(msg, "to_ip");
+   sctpEV3 << "Binding to UDP port " << SCTP_OVER_UDP_UDPPORT << endl;
+
+   cMessage *msg = new cMessage("UDP_C_BIND", UDP_C_BIND);
+   UDPControlInfo *ctrl = new UDPControlInfo();
+   ctrl->setSrcPort(SCTP_OVER_UDP_UDPPORT);
+   ctrl->setSockId(UDPSocket::generateSocketId());
+   msg->setControlInfo(ctrl);
+   send(msg, "to_ip");
 }
 
 void SCTP::initialize()
 {
-    nextEphemeralPort = (uint16)(intrand(10000) + 30000);
-
-    //sctpEV3<<"SCTPMain initialize\n";
-
-    cModule *netw = simulation.getSystemModule();
-
-    testing = netw->hasPar("testing") && netw->par("testing").boolValue();
-    if(testing) {
-    }
-    if (netw->hasPar("testTimeout"))
-    {
-        testTimeout = (simtime_t)netw->par("testTimeout");
-    }
-    numPacketsReceived = 0;
-    numPacketsDropped    = 0;
-    sizeConnMap          = 0;
-    if ((bool)par("udpEncapsEnabled"))
-        bindPortForUDP();
+   nextEphemeralPort = (uint16)(intrand(10000) + 30000);
+   cModule* netw     = simulation.getSystemModule();
+
+   checkQueues = par("checkQueues");
+   testing = netw->hasPar("testing") && netw->par("testing").boolValue();
+   if(testing) {
+      checkQueues = true;   // Always check queues in testing mode.
+   }
+   if (netw->hasPar("testTimeout")) {
+      testTimeout = (simtime_t)netw->par("testTimeout");
+   }
+   this->auth    = (bool)par("auth");
+   this->pktdrop = (bool)par("packetDrop");
+   this->sackNow = (bool)par("sackNow");
+   numPktDropReports  = 0;
+   numPacketsReceived = 0;
+   numPacketsDropped  = 0;
+   sizeAssocMap       = 0;
+   if ((bool)par("udpEncapsEnabled")) {
+      bindPortForUDP();
+   }
+#ifdef HAVE_GETTIMEOFDAY
+   StartupTime = getMicroTime();
+#endif
 }
 
-
 SCTP::~SCTP()
 {
-    sctpEV3<<"delete SCTPMain\n";
-    if (!(sctpAppConnMap.empty()))
-    {
-        sctpEV3<<"clear appConnMap ptr="<<&sctpAppConnMap<<"\n";
-        sctpAppConnMap.clear();
-    }
-    if (!(assocStatMap.empty()))
-    {
-        sctpEV3<<"clear assocStatMap ptr="<<&assocStatMap<<"\n";
-        assocStatMap.clear();
-    }
-    if (!(sctpVTagMap.empty()))
-    {
-        sctpVTagMap.clear();
-    }
-    sctpEV3<<"after clearing maps\n";
+   if (!(sctpAppAssocMap.empty())) {
+      sctpAppAssocMap.clear();
+   }
+   if (!(assocStatMap.empty())) {
+      assocStatMap.clear();
+   }
+   if (!(sctpVTagMap.empty())) {
+      sctpVTagMap.clear();
+   }
 }
 
 
 void SCTP::handleMessage(cMessage *msg)
 {
-    IPvXAddress destAddr;
-    IPvXAddress srcAddr;
-    IPControlInfo *controlInfo =NULL;
-    IPv6ControlInfo *controlInfoV6 =NULL;
-    bool findListen = false;
-    bool bitError = false;
-
-    sctpEV3<<"\n\nSCTPMain handleMessage at "<<getFullPath()<<"\n";
-
-    if (msg->isSelfMessage())
-    {
-
-        sctpEV3<<"selfMessage\n";
-
-         SCTPAssociation *assoc = (SCTPAssociation *) msg->getContextPointer();
-        bool ret = assoc->processTimer(msg);
-
-        if (!ret)
-            removeAssociation(assoc);
-    }
-    else if (msg->arrivedOn("from_ip") || msg->arrivedOn("from_ipv6"))
-    {
-        sctpEV3<<"Message from IP\n";
-        printInfoConnMap();
-        if (!dynamic_cast<SCTPMessage *>(msg))
-        {
-            sctpEV3<<"no sctp message, delete it\n";
-            delete msg;
-            return;
-        }
-        SCTPMessage *sctpmsg = check_and_cast<SCTPMessage *>(msg);
-
-        numPacketsReceived++;
-
-        if ((sctpmsg->hasBitError() || !(sctpmsg->getChecksumOk()))) {
-            sctpEV3<<"Packet has bit-error. delete it\n";
-
-            bitError = true;
-            numPacketsDropped++;
-            delete msg;
-            return;
-        }
-        if (msg->arrivedOn("from_ip"))
-        {
-            if (par("udpEncapsEnabled"))
-            {
-                std::cout<<"Laenge SCTPMSG="<<sctpmsg->getByteLength()<<"\n";
-                UDPControlInfo *ctrl = check_and_cast<UDPControlInfo *>(msg->removeControlInfo());
-                srcAddr = ctrl->getSrcAddr();
-                destAddr = ctrl->getDestAddr();
-                std::cout<<"controlInfo srcAddr="<<srcAddr<<"  destAddr="<<destAddr<<"\n";
-                std::cout<<"VTag="<<sctpmsg->getTag()<<"\n";
-            }
-            else
-            {
-                controlInfo = check_and_cast<IPControlInfo *>(msg->removeControlInfo());
-                IPDatagram *datagram = controlInfo->removeOrigDatagram();
-                delete datagram;
-                sctpEV3<<"controlInfo srcAddr="<<controlInfo->getSrcAddr()<<"   destAddr="<<controlInfo->getDestAddr()<<"\n";
-                srcAddr = controlInfo->getSrcAddr();
-                destAddr = controlInfo->getDestAddr();
+   IPvXAddress      destAddr;
+   IPvXAddress      srcAddr;
+   IPControlInfo*   controlInfo   = NULL;
+   IPv6ControlInfo* controlInfoV6 = NULL;
+   bool findListen = false;
+   bool bitError   = false;
+
+   sctpEV3 << "SCTP::handleMessage at " << getFullPath() << endl;
+
+   if (msg->isSelfMessage()) {
+      SCTPAssociation* assoc = (SCTPAssociation*)msg->getContextPointer();
+      const bool ret = assoc->processTimer(msg);
+      if (!ret) {
+         removeAssociation(assoc);
+      }
+   }
+   else if (msg->arrivedOn("from_ip") || msg->arrivedOn("from_ipv6")) {
+      sctpEV3 << "Message from IP" << endl;
+      printInfoAssocMap();
+      if (!dynamic_cast<SCTPMessage *>(msg)) {
+         sctpEV3 << "no sctp message, delete it" << endl;
+         delete msg;
+         return;
+      }
+      SCTPMessage* sctpmsg = check_and_cast<SCTPMessage*>(msg);
+
+      numPacketsReceived++;
+
+      if (!pktdrop && (sctpmsg->hasBitError() || !(sctpmsg->getChecksumOk()))) {
+         sctpEV3 << "Packet has bit-error. delete it" << endl;
+         bitError = true;
+         numPacketsDropped++;
+         delete msg;
+         return;
+      }
+      if (msg->arrivedOn("from_ip")) {
+         if (par("udpEncapsEnabled")) {
+            UDPControlInfo* ctrl = check_and_cast<UDPControlInfo *>(msg->removeControlInfo());
+            srcAddr              = ctrl->getSrcAddr();
+            destAddr             = ctrl->getDestAddr();
+         }
+         else {
+            controlInfo          = check_and_cast<IPControlInfo *>(msg->removeControlInfo());
+            IPDatagram* datagram = controlInfo->removeOrigDatagram();
+            delete datagram;
+            srcAddr  = controlInfo->getSrcAddr();
+            destAddr = controlInfo->getDestAddr();
+         }
+      }
+      else
+      {
+         controlInfoV6 = check_and_cast<IPv6ControlInfo *>(msg->removeControlInfo());
+         srcAddr  = controlInfoV6->getSrcAddr();
+         destAddr = controlInfoV6->getDestAddr();
+      }
+
+      if (sctpmsg->getBitLength()>(SCTP_COMMON_HEADER*8)) {
+         if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==INIT ||
+             ((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==INIT_ACK ||
+             ((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==PKTDROP) {
+            findListen = true;
+         }
+
+         SCTPAssociation* assoc = findAssocForMessage(srcAddr, destAddr, sctpmsg->getSrcPort(),sctpmsg->getDestPort(), findListen);
+         if (!assoc && sctpAssocMap.size()>0)
+            assoc = findAssocWithVTag(sctpmsg->getTag(),sctpmsg->getSrcPort(), sctpmsg->getDestPort());
+         if (!assoc) {
+            if (bitError) {
+               delete sctpmsg;
+               return;
             }
-        }
-        else
-        {
-            controlInfoV6 = check_and_cast<IPv6ControlInfo *>(msg->removeControlInfo());
-            srcAddr = controlInfoV6->getSrcAddr();
-            destAddr = controlInfoV6->getDestAddr();
-        }
-
-
-        sctpEV3<<"srcAddr="<<srcAddr<<" destAddr="<<destAddr<<"\n";
-        if (sctpmsg->getBitLength()>(SCTP_COMMON_HEADER*8))
-        {
-            if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==INIT || ((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==INIT_ACK )
-                findListen = true;
-
-            SCTPAssociation *assoc = findAssocForMessage(srcAddr, destAddr, sctpmsg->getSrcPort(),sctpmsg->getDestPort(), findListen);
-            if (!assoc && sctpConnMap.size()>0)
-                assoc = findAssocWithVTag(sctpmsg->getTag(),sctpmsg->getSrcPort(), sctpmsg->getDestPort());
-            if (!assoc)
-            {
-                sctpEV3<<"no assoc found msg="<<sctpmsg->getName()<<"\n";
-                if (bitError)
-                {
-                    delete sctpmsg;
-                    return;
-                }
-                if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==SHUTDOWN_ACK)
-                    sendShutdownCompleteFromMain(sctpmsg, destAddr, srcAddr);
-                else if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()!=ABORT  &&
-                    ((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()!=SHUTDOWN_COMPLETE)
-                {
-                    sendAbortFromMain(sctpmsg, destAddr, srcAddr);
-                }
-                delete sctpmsg;
+            if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==SHUTDOWN_ACK) {
+               sendShutdownCompleteFromMain(sctpmsg, destAddr, srcAddr);
             }
-            else
-            {
-                bool ret = assoc->processSCTPMessage(sctpmsg, srcAddr, destAddr);
-                if (!ret)
-                {
-                    sctpEV3<<"SCTPMain:: removeAssociation \n";
-                    removeAssociation(assoc);
-                    delete sctpmsg;
-                }
-                else
-                {
-                    delete sctpmsg;
-                }
+            else if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()!=ABORT  &&
+                     ((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()!=SHUTDOWN_COMPLETE) {
+               sendAbortFromMain(sctpmsg, destAddr, srcAddr);
             }
-        }
-        else
-        {
             delete sctpmsg;
-        }
-    }
-    else // must be from app
-    {
-        sctpEV3<<"must be from app\n";
-        SCTPCommand *controlInfo = check_and_cast<SCTPCommand *>(msg->getControlInfo());
-
-        int32 appGateIndex;
-        if (controlInfo->getGate()!=-1)
-            appGateIndex = controlInfo->getGate();
-        else
-            appGateIndex = msg->getArrivalGate()->getIndex();
-        int32 assocId = controlInfo->getAssocId();
-        sctpEV3<<"msg arrived from app for assoc "<<assocId<<"\n";
-        SCTPAssociation *assoc = findAssocForApp(appGateIndex, assocId);
-
-        if (!assoc)
-        {
-            sctpEV3 << "no assoc found. msg="<<msg->getName()<<" number of assocs = "<<assocList.size()<<"\n";
-
-            if (strcmp(msg->getName(),"PassiveOPEN")==0 || strcmp(msg->getName(),"Associate")==0)
-            {
-                if (assocList.size()>0)
-                {
-                    assoc = NULL;
-                    SCTPOpenCommand* open = check_and_cast<SCTPOpenCommand*>(controlInfo);
-                    sctpEV3<<"Looking for assoc with remoteAddr="<<open->getRemoteAddr()<<", remotePort="<<open->getRemotePort()<<", localPort="<<open->getLocalPort()<<"\n";
-                    for (std::list<SCTPAssociation*>::iterator iter=assocList.begin(); iter!=assocList.end(); iter++)
-                    {
-                        sctpEV3<<"remoteAddr="<<(*iter)->remoteAddr<<", remotePort="<<(*iter)->remotePort<<", localPort="<<(*iter)->localPort<<"\n";
-                        if ((*iter)->remoteAddr == open->getRemoteAddr() && (*iter)->localPort==open->getLocalPort() && (*iter)->remotePort==open->getRemotePort())
-                        {
-                            assoc = (*iter);
-                            break;
-                        }
-                    }
-                }
-                if (assoc==NULL)
-                {
-                    assoc = new SCTPAssociation(this,appGateIndex,assocId);
-
-                    AppConnKey key;
-                    key.appGateIndex = appGateIndex;
-                    key.assocId = assocId;
-                    sctpAppConnMap[key] = assoc;
-                    sctpEV3 << "SCTP association created for appGateIndex " << appGateIndex << " and assoc "<<assocId<<"\n";
-                    bool ret = assoc->processAppCommand(PK(msg));
-                    if (!ret)
-                    {
-                        removeAssociation(assoc);
-                    }
-                }
+         }
+         else
+         {
+            bool ret = assoc->processSCTPMessage(sctpmsg, srcAddr, destAddr);
+            if (!ret) {
+               removeAssociation(assoc);
+               delete sctpmsg;
+            }
+            else {
+               delete sctpmsg;
+            }
+         }
+      }
+      else {
+         delete sctpmsg;
+      }
+   }
+   else // must be from app
+   {
+      SCTPCommand* controlInfo = check_and_cast<SCTPCommand *>(msg->getControlInfo());
+
+      int32 appGateIndex;
+      if (controlInfo->getGate() != -1) {
+         appGateIndex = controlInfo->getGate();
+      }
+      else {
+         appGateIndex = msg->getArrivalGate()->getIndex();
+      }
+      int32 assocId = controlInfo->getAssocId();
+      sctpEV3 << "msg arrived from app for assoc " << assocId << endl;
+
+      SCTPAssociation* assoc = findAssocForApp(appGateIndex, assocId);
+      if (!assoc) {
+         sctpEV3 << "no assoc found. msg=" << msg->getName()
+                 << " number of assocs = " << assocList.size() << endl;
+
+         if ((strcmp(msg->getName(),"PassiveOPEN")==0) ||
+             (strcmp(msg->getName(),"Associate")==0)) {
+            if (assocList.size() > 0) {
+               assoc = NULL;
+               SCTPOpenCommand* open = check_and_cast<SCTPOpenCommand*>(controlInfo);
+               sctpEV3 << "Looking for assoc with remoteAddr="<<open->getRemoteAddr()<<", remotePort="<<open->getRemotePort()<<", localPort="<<open->getLocalPort()<< endl;
+               for (std::list<SCTPAssociation*>::iterator iter=assocList.begin(); iter!=assocList.end(); iter++)
+               {
+                  sctpEV3 << "remoteAddr="<<(*iter)->remoteAddr<<", remotePort="<<(*iter)->remotePort<<", localPort="<<(*iter)->localPort<< endl;
+                  if ((*iter)->remoteAddr == open->getRemoteAddr() && (*iter)->localPort==open->getLocalPort() && (*iter)->remotePort==open->getRemotePort())
+                  {
+                     assoc = (*iter);
+                     break;
+                  }
+               }
+            }
+            if (assoc==NULL) {
+               assoc = new SCTPAssociation(this,appGateIndex,assocId);
+
+               AppAssocKey key;
+               key.appGateIndex    = appGateIndex;
+               key.assocId         = assocId;
+               sctpAppAssocMap[key] = assoc;
+               sctpEV3 << "SCTP association created for appGateIndex " << appGateIndex
+                       << " and assoc "<<assocId << endl;
+
+               const bool ret = assoc->processAppCommand(PK(msg));
+               if (!ret) {
+                  removeAssociation(assoc);
+               }
             }
-        }
-        else
-        {
-            sctpEV3<<"assoc found\n";
-            bool ret = assoc->processAppCommand(PK(msg));
-
-            if (!ret)
-                removeAssociation(assoc);
-        }
-        delete msg;
-    }
-    if (ev.isGUI())
-        updateDisplayString();
+         }
+      }
+      else
+      {
+         sctpEV3 << "assoc found" << endl;
+         const bool ret = assoc->processAppCommand(PK(msg));
+         if (!ret) {
+            removeAssociation(assoc);
+         }
+      }
+      delete msg;
+   }
+   if (ev.isGUI()) {
+      updateDisplayString();
+   }
 }
 
-void SCTP::sendAbortFromMain(SCTPMessage* sctpmsg, IPvXAddress srcAddr, IPvXAddress destAddr)
+void SCTP::sendAbortFromMain(SCTPMessage*       sctpmsg,
+                             const IPvXAddress& srcAddr,
+                             const IPvXAddress& destAddr)
 {
-    SCTPMessage *msg = new SCTPMessage();
-
-    sctpEV3<<"\n\nSCTPMain:sendABORT \n";
-
-    msg->setSrcPort(sctpmsg->getDestPort());
-    msg->setDestPort(sctpmsg->getSrcPort());
-    msg->setBitLength(SCTP_COMMON_HEADER*8);
-    msg->setChecksumOk(true);
-
-    SCTPAbortChunk* abortChunk = new SCTPAbortChunk("ABORT");
-    abortChunk->setChunkType(ABORT);
-    if (sctpmsg->getChunksArraySize()>0 && ((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==INIT)
-    {
-
-        SCTPInitChunk* initChunk = check_and_cast<SCTPInitChunk *>(sctpmsg->getChunks(0));
-        abortChunk->setT_Bit(0);
-        msg->setTag(initChunk->getInitTag());
-    }
-    else
-    {
-        abortChunk->setT_Bit(1);
-        msg->setTag(sctpmsg->getTag());
-    }
-    abortChunk->setBitLength(SCTP_ABORT_CHUNK_LENGTH*8);
-    msg->addChunk(abortChunk);
-    if ((bool)par("udpEncapsEnabled"))
-    {
-        msg->setKind(UDP_C_DATA);
-        std::cout<<"VTag="<<msg->getTag()<<"\n";
-        UDPControlInfo *ctrl = new UDPControlInfo();
-        ctrl->setSrcPort(9899);
-        ctrl->setDestAddr(destAddr.get4());
-        ctrl->setDestPort(9899);
-        msg->setControlInfo(ctrl);
-    }
-    else
-    {
-        IPControlInfo *controlInfo = new IPControlInfo();
-        controlInfo->setProtocol(IP_PROT_SCTP);
-        controlInfo->setSrcAddr(srcAddr.get4());
-        controlInfo->setDestAddr(destAddr.get4());
-        msg->setControlInfo(controlInfo);
-    }
-    send(msg,"to_ip");
+   SCTPMessage* msg = new SCTPMessage();
+
+   sctpEV3 << "SCTP::sendAbortFromMain" << endl;
+
+   msg->setSrcPort(sctpmsg->getDestPort());
+   msg->setDestPort(sctpmsg->getSrcPort());
+   msg->setBitLength(SCTP_COMMON_HEADER*8);
+   msg->setChecksumOk(true);
+
+   SCTPAbortChunk* abortChunk = new SCTPAbortChunk("ABORT");
+   abortChunk->setChunkType(ABORT);
+   if ((sctpmsg->getChunksArraySize()>0) &&
+       (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType()==INIT)) {
+
+      SCTPInitChunk* initChunk = check_and_cast<SCTPInitChunk *>(sctpmsg->getChunks(0));
+      abortChunk->setT_Bit(0);
+      msg->setTag(initChunk->getInitTag());
+   }
+   else {
+      abortChunk->setT_Bit(1);
+      msg->setTag(sctpmsg->getTag());
+   }
+   abortChunk->setBitLength(SCTP_ABORT_CHUNK_LENGTH*8);
+   msg->addChunk(abortChunk);
+   if ((bool)par("udpEncapsEnabled")) {
+      msg->setKind(UDP_C_DATA);
+      std::cout<<"VTag="<<msg->getTag()<< endl;
+      UDPControlInfo *ctrl = new UDPControlInfo();
+      ctrl->setSrcPort(SCTP_OVER_UDP_UDPPORT);
+      ctrl->setDestAddr(destAddr.get4());
+      ctrl->setDestPort(SCTP_OVER_UDP_UDPPORT);
+      msg->setControlInfo(ctrl);
+   }
+   else {
+      IPControlInfo *controlInfo = new IPControlInfo();
+      controlInfo->setProtocol(IP_PROT_SCTP);
+      controlInfo->setSrcAddr(srcAddr.get4());
+      controlInfo->setDestAddr(destAddr.get4());
+      msg->setControlInfo(controlInfo);
+   }
+   send(msg,"to_ip");
 }
 
-void SCTP::sendShutdownCompleteFromMain(SCTPMessage* sctpmsg, IPvXAddress srcAddr, IPvXAddress destAddr)
+void SCTP::sendShutdownCompleteFromMain(SCTPMessage*       sctpmsg,
+                                        const IPvXAddress& srcAddr,
+                                        const IPvXAddress& destAddr)
 {
-    SCTPMessage *msg = new SCTPMessage();
-
-    sctpEV3<<"\n\nSCTP:sendABORT \n";
-
-    msg->setSrcPort(sctpmsg->getDestPort());
-    msg->setDestPort(sctpmsg->getSrcPort());
-    msg->setBitLength(SCTP_COMMON_HEADER*8);
-    msg->setChecksumOk(true);
-
-    SCTPShutdownCompleteChunk* scChunk = new SCTPShutdownCompleteChunk("SHUTDOWN_COMPLETE");
-    scChunk->setChunkType(SHUTDOWN_COMPLETE);
-    scChunk->setTBit(1);
-    msg->setTag(sctpmsg->getTag());
-
-    scChunk->setBitLength(SCTP_SHUTDOWN_ACK_LENGTH*8);
-    msg->addChunk(scChunk);
-    IPControlInfo *controlInfo = new IPControlInfo();
-    controlInfo->setProtocol(IP_PROT_SCTP);
-    controlInfo->setSrcAddr(srcAddr.get4());
-    controlInfo->setDestAddr(destAddr.get4());
-    msg->setControlInfo(controlInfo);
-    send(msg,"to_ip");
+   SCTPMessage* msg = new SCTPMessage();
+
+   sctpEV3 << "SCTP::sendShutdownCompleteFromMain" << endl;
+
+   msg->setSrcPort(sctpmsg->getDestPort());
+   msg->setDestPort(sctpmsg->getSrcPort());
+   msg->setBitLength(SCTP_COMMON_HEADER*8);
+   msg->setChecksumOk(true);
+
+   SCTPShutdownCompleteChunk* scChunk = new SCTPShutdownCompleteChunk("SHUTDOWN_COMPLETE");
+   scChunk->setChunkType(SHUTDOWN_COMPLETE);
+   scChunk->setTBit(1);
+   msg->setTag(sctpmsg->getTag());
+
+   scChunk->setBitLength(SCTP_SHUTDOWN_ACK_LENGTH*8);
+   msg->addChunk(scChunk);
+   IPControlInfo *controlInfo = new IPControlInfo();
+   controlInfo->setProtocol(IP_PROT_SCTP);
+   controlInfo->setSrcAddr(srcAddr.get4());
+   controlInfo->setDestAddr(destAddr.get4());
+   msg->setControlInfo(controlInfo);
+
+   send(msg,"to_ip");
 }
 
 
 void SCTP::updateDisplayString()
 {
-    if (ev.disable_tracing)
-    {
-        // in express mode, we don't bother to update the display
-        // (std::map's iteration is not very fast if map is large)
-        getDisplayString().setTagArg("t",0,"");
-        return;
-    }
+   if (ev.disable_tracing)
+   {
+      // in express mode, we don't bother to update the display
+      // (std::map's iteration is not very fast if map is large)
+      getDisplayString().setTagArg("t",0,"");
+      return;
+   }
 
 }
 
-SCTPAssociation *SCTP::findAssocWithVTag(uint32 peerVTag, uint32 remotePort, uint32 localPort)
+SCTPAssociation* SCTP::findAssocWithVTag(const uint32 peerVTag,
+                                         const uint32 remotePort,
+                                         const uint32 localPort)
 {
-
-    printVTagMap();
-    sctpEV3<<"findAssocWithVTag: peerVTag="<<peerVTag<<" srcPort="<<remotePort<<"    destPort="<<localPort<<"\n";
-    printInfoConnMap();
-
-    // try with fully qualified SockPair
-    for (SctpVTagMap::iterator i=sctpVTagMap.begin(); i!=sctpVTagMap.end();i++)
-    {
-        if ((i->second.peerVTag==peerVTag && i->second.localPort==localPort
-            && i->second.remotePort==remotePort)
-            || (i->second.localVTag==peerVTag && i->second.localPort==localPort
-            && i->second.remotePort==remotePort))
-            return getAssoc(i->first);
-    }
-    return NULL;
+   printVTagMap();
+   sctpEV3 << "findAssocWithVTag: peerVTag=" << peerVTag
+           << " srcPort="  << remotePort
+           << " destPort=" << localPort << endl;
+   printInfoAssocMap();
+
+   // try with fully qualified SockPair
+   for (SctpVTagMap::iterator i=sctpVTagMap.begin(); i!=sctpVTagMap.end();i++) {
+      if ((i->second.peerVTag==peerVTag && i->second.localPort==localPort
+         && i->second.remotePort==remotePort)
+         || (i->second.localVTag==peerVTag && i->second.localPort==localPort
+         && i->second.remotePort==remotePort))
+         return getAssoc(i->first);
+   }
+   return NULL;
 }
 
-SCTPAssociation *SCTP::findAssocForMessage(IPvXAddress srcAddr, IPvXAddress destAddr, uint32 srcPort, uint32 destPort, bool findListen)
+SCTPAssociation* SCTP::findAssocForMessage(const IPvXAddress& srcAddr,
+                                           const IPvXAddress& destAddr,
+                                           const uint32       srcPort,
+                                           const uint32       destPort,
+                                           const bool         findListen)
 {
-    SockPair key;
-
-    key.localAddr = destAddr;
-    key.remoteAddr = srcAddr;
-    key.localPort = destPort;
-    key.remotePort = srcPort;
-    SockPair save = key;
-    sctpEV3<<"findAssocForMessage: srcAddr="<<destAddr<<" destAddr="<<srcAddr<<" srcPort="<<destPort<<"  destPort="<<srcPort<<"\n";
-    printInfoConnMap();
-
-    // try with fully qualified SockPair
-    SctpConnMap::iterator i;
-    i = sctpConnMap.find(key);
-    if (i!=sctpConnMap.end())
-        return i->second;
-
-
-    // try with localAddr missing (only localPort specified in passive/active open)
-    key.localAddr.set("0.0.0.0");
-
-    i = sctpConnMap.find(key);
-    if (i!=sctpConnMap.end())
-    {
-
-        //sctpEV3<<"try with localAddr missing (only localPort specified in passive/active open)\n";
-
-        return i->second;
-    }
-
-    if (findListen==true)
-    {
-        /*key = save;
-        key.localPort = 0;
-        key.localAddr.set("0.0.0.0");
-        i = sctpConnMap.find(key);
-        if (i!=sctpConnMap.end())
-        {
-            return i->second;
-        }*/
-
-
-        // try fully qualified local socket + blank remote socket (for incoming SYN)
-        key = save;
-        key.remoteAddr.set("0.0.0.0");
-        key.remotePort = 0;
-        i = sctpConnMap.find(key);
-        if (i!=sctpConnMap.end())
-        {
-
-            //sctpEV3<<"try fully qualified local socket + blank remote socket \n";
-
-            return i->second;
-        }
-
-
-        // try with blank remote socket, and localAddr missing (for incoming SYN)
-        key.localAddr.set("0.0.0.0");
-        i = sctpConnMap.find(key);
-        if (i!=sctpConnMap.end())
-        {
-
-            //sctpEV3<<"try with blank remote socket, and localAddr missing \n";
-
-            return i->second;
-        }
-    }
-    // given up
-
-    sctpEV3<<"giving up on trying to find assoc for localAddr="<<srcAddr<<" remoteAddr="<<destAddr<<" localPort="<<srcPort<<" remotePort="<<destPort<<"\n";
-    return NULL;
+   SockPair key;
+
+   key.localAddr  = destAddr;
+   key.remoteAddr = srcAddr;
+   key.localPort  = destPort;
+   key.remotePort = srcPort;
+   SockPair save = key;
+   sctpEV3 << "findAssocForMessage: localAddr=" << destAddr << " remoteAddr=" << srcAddr
+           << " localPort=" << destPort << " remotePort=" << srcPort << endl;
+   printInfoAssocMap();
+
+   // try with fully qualified SockPair
+   SctpAssocMap::iterator i;
+   i = sctpAssocMap.find(key);
+   if (i!=sctpAssocMap.end())
+      return i->second;
+
+
+   // try with localAddr missing (only localPort specified in passive/active open)
+   key.localAddr.set("0.0.0.0");
+
+   i = sctpAssocMap.find(key);
+   if (i!=sctpAssocMap.end()) {
+      return i->second;
+   }
+
+   if (findListen == true) {
+      // try fully qualified local socket + blank remote socket (for incoming SYN)
+      key = save;
+      key.remoteAddr.set("0.0.0.0");
+      key.remotePort = 0;
+      i = sctpAssocMap.find(key);
+      if (i!=sctpAssocMap.end()) {
+         return i->second;
+      }
+
+      // try with blank remote socket, and localAddr missing (for incoming SYN)
+      key.localAddr.set("0.0.0.0");
+      i = sctpAssocMap.find(key);
+      if (i != sctpAssocMap.end()) {
+         return i->second;
+      }
+   }
+
+   // given up
+   sctpEV3 << "giving up on trying to find assoc for localAddr=" << destAddr << " remoteAddr=" << srcAddr
+           << " localPort=" << destPort << " remotePort=" << srcPort << endl;
+   return NULL;
 }
 
-SCTPAssociation *SCTP::findAssocForApp(int32 appGateIndex, int32 assocId)
+SCTPAssociation* SCTP::findAssocForApp(const int32 appGateIndex,
+                                       const int32 assocId)
 {
-    AppConnKey key;
-    key.appGateIndex = appGateIndex;
-    key.assocId = assocId;
-    sctpEV3<<"findAssoc for appGateIndex "<<appGateIndex<<" and assoc "<<assocId<<"\n";
-    SctpAppConnMap::iterator i = sctpAppConnMap.find(key);
-    return i==sctpAppConnMap.end() ? NULL : i->second;
+   AppAssocKey key;
+   key.appGateIndex = appGateIndex;
+   key.assocId      = assocId;
+   sctpEV3 << "findAssoc for appGateIndex "<<appGateIndex<<" and assoc "<<assocId<< endl;
+   SctpAppAssocMap::iterator i = sctpAppAssocMap.find(key);
+   return((i == sctpAppAssocMap.end()) ? NULL : i->second);
 }
 
-uint16 SCTP::getEphemeralPort()
+int16 SCTP::getEphemeralPort()
 {
-    if (nextEphemeralPort==5000)
-        error("Ephemeral port range 1024..4999 exhausted (email SCTP model "
-                "author that he should implement reuse of ephemeral ports!!!)");
-    return nextEphemeralPort++;
+   if (nextEphemeralPort == 5000) {
+      error("Ephemeral port range 1024..4999 exhausted (email SCTP model "
+            "author that he should implement reuse of ephemeral ports!!!)");
+   }
+   return nextEphemeralPort++;
 }
 
-void SCTP::updateSockPair(SCTPAssociation *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int32 localPort, int32 remotePort)
+void SCTP::updateSockPair(SCTPAssociation*   assoc,
+                          const IPvXAddress& localAddr,
+                          const IPvXAddress& remoteAddr,
+                          const int32        localPort,
+                          const int32        remotePort)
 {
-    SockPair key;
-    sctpEV3<<"updateSockPair:   localAddr: "<<localAddr<<"   remoteAddr="<<remoteAddr<<"    localPort="<<localPort<<" remotePort="<<remotePort<<"\n";
-
-    key.localAddr = (conn->localAddr = localAddr);
-    key.remoteAddr = (conn->remoteAddr = remoteAddr);
-    key.localPort = conn->localPort = localPort;
-    key.remotePort = conn->remotePort = remotePort;
-
-    for (SctpConnMap::iterator i=sctpConnMap.begin(); i!=sctpConnMap.end(); i++)
-    {
-        if (i->second == conn)
-        {
-            sctpConnMap.erase(i);
-            break;
-        }
-    }
-
-    sctpEV3<<"updateSockPair conn="<<conn<<"    localAddr="<<key.localAddr<<"            remoteAddr="<<key.remoteAddr<<"     localPort="<<key.localPort<<"  remotePort="<<remotePort<<"\n";
-
-    sctpConnMap[key] = conn;
-    sizeConnMap = sctpConnMap.size();
-    //sctpEV3<<"number of connections="<<sctpConnMap.size()<<"\n";
-    sctpEV3<<"assoc inserted in sctpConnMap\n";
-    printInfoConnMap();
+   sctpEV3 << "updateSockPair: localAddr=" << localAddr <<" remoteAddr=" << remoteAddr
+           <<" localPort=" << localPort << " remotePort=" << remotePort << endl;
+
+   SockPair key;
+   key.localAddr  = (assoc->localAddr = localAddr);
+   key.remoteAddr = (assoc->remoteAddr = remoteAddr);
+   key.localPort  = assoc->localPort = localPort;
+   key.remotePort = assoc->remotePort = remotePort;
+
+   for (SctpAssocMap::iterator i=sctpAssocMap.begin(); i!=sctpAssocMap.end(); i++) {
+      if (i->second == assoc) {
+         sctpAssocMap.erase(i);
+         break;
+      }
+   }
+
+   sctpAssocMap[key] = assoc;
+   sizeAssocMap = sctpAssocMap.size();
+
+   printInfoAssocMap();
 }
 
-void SCTP::addLocalAddress(SCTPAssociation *conn, IPvXAddress address)
+void SCTP::addLocalAddress(SCTPAssociation*   assoc,
+                           const IPvXAddress& address)
 {
-
-        //sctpEV3<<"Add local address: "<<address<<"\n";
-
-        SockPair key;
-
-        key.localAddr = conn->localAddr;
-        key.remoteAddr = conn->remoteAddr;
-        key.localPort = conn->localPort;
-        key.remotePort = conn->remotePort;
-
-        SctpConnMap::iterator i = sctpConnMap.find(key);
-        if (i!=sctpConnMap.end())
-        {
-            ASSERT(i->second==conn);
-            if (key.localAddr.isUnspecified())
-            {
-                sctpConnMap.erase(i);
-                sizeConnMap--;
-            }
-        }
-        else
-            sctpEV3<<"no actual sockPair found\n";
-        key.localAddr = address;
-        //key.localAddr = address.get4().getInt();
-        // //sctpEV3<<"laddr="<<key.localAddr<<"    lp="<<key.localPort<<"  raddr="<<key.remoteAddr<<" rPort="<<key.remotePort<<"\n";
-        sctpConnMap[key] = conn;
-        sizeConnMap = sctpConnMap.size();
-        sctpEV3<<"number of connections="<<sizeConnMap<<"\n";
-
-        printInfoConnMap();
+   SockPair key;
+   key.localAddr  = assoc->localAddr;
+   key.remoteAddr = assoc->remoteAddr;
+   key.localPort  = assoc->localPort;
+   key.remotePort = assoc->remotePort;
+
+   SctpAssocMap::iterator i = sctpAssocMap.find(key);
+   if (i!=sctpAssocMap.end()) {
+      ASSERT(i->second == assoc);
+      if (key.localAddr.isUnspecified()) {
+         sctpAssocMap.erase(i);
+         sizeAssocMap--;
+      }
+   }
+
+   key.localAddr = address;
+   sctpAssocMap[key] = assoc;
+   sizeAssocMap = sctpAssocMap.size();
+   sctpEV3 << "number of assocections="<<sizeAssocMap<< endl;
+
+   printInfoAssocMap();
 }
 
-void SCTP::addLocalAddressToAllRemoteAddresses(SCTPAssociation *conn, IPvXAddress address, std::vector<IPvXAddress> remAddresses)
+void SCTP::addLocalAddressToAllRemoteAddresses(SCTPAssociation*                assoc,
+                                               const IPvXAddress&              address,
+                                               const std::vector<IPvXAddress>& remAddresses)
 {
-
-        //sctpEV3<<"Add local address: "<<address<<"\n";
-
-        SockPair key;
-
-        for (AddressVector::iterator i=remAddresses.begin(); i!=remAddresses.end(); ++i)
-        {
-            //sctpEV3<<"remote address="<<(*i)<<"\n";
-            key.localAddr = conn->localAddr;
-            key.remoteAddr = (*i);
-            key.localPort = conn->localPort;
-            key.remotePort = conn->remotePort;
-
-            SctpConnMap::iterator j = sctpConnMap.find(key);
-            if (j!=sctpConnMap.end())
-            {
-            ASSERT(j->second==conn);
-            if (key.localAddr.isUnspecified())
-                    {
-                    sctpConnMap.erase(j);
-                    sizeConnMap--;
-                }
-
-            }
-            else
-                sctpEV3<<"no actual sockPair found\n";
-            key.localAddr = address;
-            sctpConnMap[key] = conn;
-
-            sizeConnMap++;
-            sctpEV3<<"number of connections="<<sctpConnMap.size()<<"\n";
-
-            printInfoConnMap();
-        }
+   SockPair key;
+   for (AddressVector::const_iterator i=remAddresses.begin(); i!=remAddresses.end(); ++i) {
+      key.localAddr = assoc->localAddr;
+      key.remoteAddr = (*i);
+      key.localPort = assoc->localPort;
+      key.remotePort = assoc->remotePort;
+
+      SctpAssocMap::iterator j = sctpAssocMap.find(key);
+      if (j!=sctpAssocMap.end()) {
+         ASSERT(j->second==assoc);
+         if (key.localAddr.isUnspecified()) {
+            sctpAssocMap.erase(j);
+            sizeAssocMap--;
+         }
+      }
+
+      key.localAddr = address;
+      sctpAssocMap[key] = assoc;
+      sizeAssocMap++;
+      sctpEV3 << "number of assocections=" << sctpAssocMap.size() << endl;
+
+      printInfoAssocMap();
+   }
 }
 
-void SCTP::removeLocalAddressFromAllRemoteAddresses(SCTPAssociation *conn, IPvXAddress address, std::vector<IPvXAddress> remAddresses)
+void SCTP::removeLocalAddressFromAllRemoteAddresses(SCTPAssociation*                assoc,
+                                                    const IPvXAddress&              address,
+                                                    const std::vector<IPvXAddress>& remAddresses)
 {
-
-        //sctpEV3<<"Remove local address: "<<address<<"\n";
-
-        SockPair key;
-
-        for (AddressVector::iterator i=remAddresses.begin(); i!=remAddresses.end(); ++i)
-        {
-            //sctpEV3<<"remote address="<<(*i)<<"\n";
-            key.localAddr = address;
-            key.remoteAddr = (*i);
-            key.localPort = conn->localPort;
-            key.remotePort = conn->remotePort;
-
-            SctpConnMap::iterator j = sctpConnMap.find(key);
-            if (j!=sctpConnMap.end())
-            {
-                ASSERT(j->second==conn);
-                sctpConnMap.erase(j);
-                sizeConnMap--;
-            }
-            else
-                sctpEV3<<"no actual sockPair found\n";
-
-            //sctpEV3<<"number of connections="<<sctpConnMap.size()<<"\n";
-
-            printInfoConnMap();
-        }
+   SockPair key;
+   for (AddressVector::const_iterator i=remAddresses.begin(); i!=remAddresses.end(); ++i) {
+      key.localAddr  = address;
+      key.remoteAddr = (*i);
+      key.localPort  = assoc->localPort;
+      key.remotePort = assoc->remotePort;
+
+      SctpAssocMap::iterator j = sctpAssocMap.find(key);
+      if (j!=sctpAssocMap.end()) {
+         ASSERT(j->second == assoc);
+         sctpAssocMap.erase(j);
+         sizeAssocMap--;
+      }
+
+      printInfoAssocMap();
+   }
 }
 
-void SCTP::removeRemoteAddressFromAllConnections(SCTPAssociation *conn, IPvXAddress address, std::vector<IPvXAddress> locAddresses)
+void SCTP::removeRemoteAddressFromAllAssociations(SCTPAssociation*                assoc,
+                                                  const IPvXAddress&              address,
+                                                  const std::vector<IPvXAddress>& locAddresses)
 {
+   SockPair key;
+   for (AddressVector::const_iterator i=locAddresses.begin(); i!=locAddresses.end(); i++) {
+      key.localAddr  = (*i);
+      key.remoteAddr = address;
+      key.localPort  = assoc->localPort;
+      key.remotePort = assoc->remotePort;
+
+      SctpAssocMap::iterator j = sctpAssocMap.find(key);
+      if (j!=sctpAssocMap.end()) {
+         ASSERT(j->second == assoc);
+         sctpAssocMap.erase(j);
+         sizeAssocMap--;
+      }
+
+      printInfoAssocMap();
+   }
+}
 
-        //sctpEV3<<"Remove remote address: "<<address<<"\n";
-
-        SockPair key;
+void SCTP::addRemoteAddress(SCTPAssociation*   assoc,
+                            const IPvXAddress& localAddress,
+                            const IPvXAddress& remoteAddress)
+{
+   sctpEV3 << "Add remote address " << remoteAddress
+           << " to local address " << localAddress << endl;
+
+   SockPair key;
+   key.localAddr  = localAddress;
+   key.remoteAddr = remoteAddress;
+   key.localPort  = assoc->localPort;
+   key.remotePort = assoc->remotePort;
+
+   SctpAssocMap::iterator i = sctpAssocMap.find(key);
+   if (i!=sctpAssocMap.end()) {
+      ASSERT(i->second == assoc);
+   }
+   else {
+      sctpAssocMap[key] = assoc;
+      sizeAssocMap++;
+   }
+
+   printInfoAssocMap();
+}
 
-        for (AddressVector::iterator i=locAddresses.begin(); i!=locAddresses.end(); i++)
-        {
-            //sctpEV3<<"local address="<<(*i)<<"\n";
-            key.localAddr = (*i);
-            key.remoteAddr = address;
-            key.localPort = conn->localPort;
-            key.remotePort = conn->remotePort;
+void SCTP::addForkedAssociation(SCTPAssociation*   assoc,
+                                SCTPAssociation*   newAssoc,
+                                const IPvXAddress& localAddr,
+                                const IPvXAddress& remoteAddr,
+                                const int32        localPort,
+                                const int32        remotePort)
+{
+   sctpEV3 << "addForkedAssociation assocId= "<< assoc->assocId
+           << " newId=" << newAssoc->assocId << endl;
+
+   SockPair keyAssoc;
+   for (SctpAssocMap::iterator j=sctpAssocMap.begin(); j!=sctpAssocMap.end(); ++j) {
+      if (assoc->assocId==j->second->assocId) {
+         keyAssoc = j->first;
+      }
+   }
+
+   // update assoc's socket pair, and register newAssoc (which'll keep LISTENing)
+   updateSockPair(assoc, localAddr, remoteAddr, localPort, remotePort);
+   updateSockPair(newAssoc, keyAssoc.localAddr, keyAssoc.remoteAddr, keyAssoc.localPort, keyAssoc.remotePort);
+
+   // assoc will get a new assocId...
+   AppAssocKey key;
+   key.appGateIndex = assoc->appGateIndex;
+   key.assocId      = assoc->assocId;
+   sctpAppAssocMap.erase(key);
+   key.assocId = assoc->assocId = getNewAssocId();
+   sctpAppAssocMap[key] = assoc;
+
+   // ...and newAssoc will live on with the old assocId
+   key.appGateIndex = newAssoc->appGateIndex;
+   key.assocId      = newAssoc->assocId;
+   sctpAppAssocMap[key] = newAssoc;
+
+   printInfoAssocMap();
+}
 
-            SctpConnMap::iterator j = sctpConnMap.find(key);
-            if (j!=sctpConnMap.end())
-            {
-                ASSERT(j->second==conn);
-                sctpConnMap.erase(j);
-                sizeConnMap--;
+void SCTP::removeAssociation(SCTPAssociation* assoc)
+{
+   bool        ok   = false;
+   bool        find = false;
+   const int32 id   = assoc->assocId;
+
+   sctpEV3 << "removeAssociation assocId= "<< id << endl;
+
+   printInfoAssocMap();
+   if (sizeAssocMap > 0) {
+      AssocStatMap::iterator assocStatMapIterator = assocStatMap.find(assoc->assocId);
+      if (assocStatMapIterator != assocStatMap.end()) {
+         assocStatMapIterator->second.stop       = simulation.getSimTime();
+         assocStatMapIterator->second.lifeTime   = assocStatMapIterator->second.stop - assocStatMapIterator->second.start;
+         assocStatMapIterator->second.throughput = assocStatMapIterator->second.ackedBytes*8 / assocStatMapIterator->second.lifeTime.dbl();
+      }
+      while (!ok) {
+         if (sizeAssocMap == 0) {
+            ok = true;
+         }
+         else {
+            for (SctpAssocMap::iterator sctpAssocMapIterator = sctpAssocMap.begin();
+                 sctpAssocMapIterator != sctpAssocMap.end(); sctpAssocMapIterator++) {
+               if (sctpAssocMapIterator->second != NULL) {
+                  SCTPAssociation* myAssoc = sctpAssocMapIterator->second;
+                  if (myAssoc->assocId == assoc->assocId) {
+                     if (myAssoc->T1_InitTimer) {
+                        myAssoc->stopTimer(myAssoc->T1_InitTimer);
+                     }
+                     if (myAssoc->T2_ShutdownTimer) {
+                        myAssoc->stopTimer(myAssoc->T2_ShutdownTimer);
+                     }
+                     if (myAssoc->T5_ShutdownGuardTimer) {
+                        myAssoc->stopTimer(myAssoc->T5_ShutdownGuardTimer);
+                     }
+                     if (myAssoc->SackTimer) {
+                        myAssoc->stopTimer(myAssoc->SackTimer);
+                     }
+                     if (myAssoc->StartAddIP) {
+                        myAssoc->stopTimer(myAssoc->StartAddIP);
+                     }
+                     sctpAssocMap.erase(sctpAssocMapIterator);
+                     sizeAssocMap--;
+                     find = true;
+                     break;
+                  }
+               }
             }
-            else
-                sctpEV3<<"no actual sockPair found\n";
-
-            //sctpEV3<<"number of connections="<<sctpConnMap.size()<<"\n";
-
-            printInfoConnMap();
-        }
+         }
+
+         if (!find) {
+            ok = true;
+         }
+         else {
+            find = false;
+         }
+      }
+   }
+
+   // T.D. 26.11.09: Write statistics
+   char str[128];
+   for (SCTPAssociation::SCTPPathMap::iterator pathMapIterator = assoc->sctpPathMap.begin();
+        pathMapIterator != assoc->sctpPathMap.end(); pathMapIterator++) {
+      const SCTPPathVariables* path = pathMapIterator->second;
+      snprintf((char*)&str, sizeof(str), "Number of Fast Retransmissions %d:%s",
+               assoc->assocId, path->remoteAddress.str().c_str());
+      recordScalar(str, path->numberOfFastRetransmissions);
+      snprintf((char*)&str, sizeof(str), "Number of Timer-Based Retransmissions %d:%s",
+               assoc->assocId, path->remoteAddress.str().c_str());
+      recordScalar(str, path->numberOfTimerBasedRetransmissions);
+      snprintf((char*)&str, sizeof(str), "Number of Duplicates %d:%s",
+               assoc->assocId, path->remoteAddress.str().c_str());
+      recordScalar(str, path->numberOfDuplicates);
+      path->statisticsPathOutstandingBytes->setUtilizationMaximum(assoc->state->sendQueueLimit);
+      path->statisticsPathQueuedSentBytes->setUtilizationMaximum(assoc->state->sendQueueLimit);
+   }
+   assoc->statisticsQueuedSentBytes->setUtilizationMaximum(assoc->state->sendQueueLimit);
+   assoc->statisticsQueuedReceivedBytes->setUtilizationMaximum((double)par("arwnd"));
+
+   assoc->removePath();
+   assoc->deleteStreams();
+
+   // TD 20.11.09: Chunks may be in the transmission and retransmission queues simultaneously.
+   //              Remove entry from transmission queue if it is already in the retransmission queue.
+   for (SCTPQueue::PayloadQueue::iterator i = assoc->getRetransmissionQueue()->payloadQueue.begin();
+        i != assoc->getRetransmissionQueue()->payloadQueue.end(); i++) {
+      SCTPQueue::PayloadQueue::iterator j = assoc->getTransmissionQueue()->payloadQueue.find(i->second->tsn);
+      if(j != assoc->getTransmissionQueue()->payloadQueue.end()) {
+         assoc->getTransmissionQueue()->payloadQueue.erase(j);
+      }
+   }
+    // TD 20.11.09: Now, both queues can be safely deleted.
+   delete assoc->getRetransmissionQueue();
+   delete assoc->getTransmissionQueue();
+
+   AppAssocKey key;
+   key.appGateIndex = assoc->appGateIndex;
+   key.assocId      = assoc->assocId;
+   sctpAppAssocMap.erase(key);
+   assocList.remove(assoc);
+   delete assoc;
 }
 
-void SCTP::addRemoteAddress(SCTPAssociation *conn, IPvXAddress localAddress, IPvXAddress remoteAddress)
+SCTPAssociation* SCTP::getAssoc(const int32 assocId)
 {
-
-    sctpEV3<<"Add remote Address: "<<remoteAddress<<" to local Address "<<localAddress<<"\n";
-
-    SockPair key;
-    key.localAddr = localAddress;
-    key.remoteAddr = remoteAddress;
-    key.localPort = conn->localPort;
-    key.remotePort = conn->remotePort;
-
-    SctpConnMap::iterator i = sctpConnMap.find(key);
-    if (i!=sctpConnMap.end())
-    {
-        ASSERT(i->second==conn);
-    }
-    else
-    {
-
-        //sctpEV3<<"no actual sockPair found\n";
-
-        sctpConnMap[key] = conn;
-        sizeConnMap++;
-    }
-
-    //sctpEV3<<"number of connections="<<sctpConnMap.size()<<"\n";
-    printInfoConnMap();
+   for (SctpAppAssocMap::iterator i = sctpAppAssocMap.begin(); i!=sctpAppAssocMap.end(); i++) {
+      if (i->first.assocId == assocId) {
+         return i->second;
+      }
+   }
+   return NULL;
 }
 
-void SCTP::addForkedAssociation(SCTPAssociation *assoc, SCTPAssociation *newAssoc, IPvXAddress localAddr, IPvXAddress remoteAddr, int32 localPort, int32 remotePort)
+void SCTP::finish()
 {
-    SockPair keyAssoc;
-
-    ev<<"addForkedConnection assocId="<<assoc->assocId<<"    newId="<<newAssoc->assocId<<"\n";
-
-    for (SctpConnMap::iterator j=sctpConnMap.begin(); j!=sctpConnMap.end(); ++j)
-        if (assoc->assocId==j->second->assocId)
-            keyAssoc = j->first;
-    // update conn's socket pair, and register newConn (which'll keep LISTENing)
-    updateSockPair(assoc, localAddr, remoteAddr, localPort, remotePort);
-    updateSockPair(newAssoc, keyAssoc.localAddr, keyAssoc.remoteAddr, keyAssoc.localPort, keyAssoc.remotePort);
-    // conn will get a new assocId...
-    AppConnKey key;
-    key.appGateIndex = assoc->appGateIndex;
-    key.assocId = assoc->assocId;
-    sctpAppConnMap.erase(key);
-    key.assocId = assoc->assocId = getNewConnId();
-    sctpAppConnMap[key] = assoc;
-
-    // ...and newConn will live on with the old assocId
-    key.appGateIndex = newAssoc->appGateIndex;
-    key.assocId = newAssoc->assocId;
-    sctpAppConnMap[key] = newAssoc;
-    /*ev<<"assocId="<<assoc->assocId<<" remoteAddr="<<assoc->remoteAddr<<"\n";
-    assoc->removeOldPath();*/
-    printInfoConnMap();
+   SctpAssocMap::iterator assocMapIterator = sctpAssocMap.begin();
+   while (assocMapIterator != sctpAssocMap.end()) {
+      removeAssociation(assocMapIterator->second);
+      assocMapIterator = sctpAssocMap.begin();
+   }
+   ev << getFullPath() << ": finishing SCTP with "
+      << sctpAssocMap.size() << " connections open." << endl;
+
+   for (AssocStatMap::const_iterator iterator = assocStatMap.begin();
+        iterator != assocStatMap.end(); iterator++) {
+      const SCTP::AssocStat& assoc = iterator->second;
+
+      ev << "Association " << assoc.assocId << ": started at " << assoc.start
+         << " and finished at " << assoc.stop << " --> lifetime: " << assoc.lifeTime << endl;
+      ev << "Association " << assoc.assocId << ": sent bytes=" << assoc.sentBytes
+         << ", acked bytes=" << assoc.ackedBytes<< ", throughput=" << assoc.throughput<< " bit/s" << endl;
+      ev << "Association " << assoc.assocId << ": transmitted Bytes="
+         << assoc.transmittedBytes<< ", retransmitted Bytes=" << assoc.transmittedBytes-assoc.ackedBytes<< endl;
+      ev << "Association " << assoc.assocId << ": number of Fast RTX="
+         << assoc.numFastRtx << ", number of Timer-Based RTX=" << assoc.numT3Rtx
+         << ", path failures=" << assoc.numPathFailures<< ", ForwardTsns=" << assoc.numForwardTsn<< endl;
+      ev << "AllMessages=" <<numPacketsReceived<< " BadMessages=" <<numPacketsDropped<< endl;
+
+      recordScalar("Association Lifetime",  assoc.lifeTime);
+      recordScalar("Acked Bytes",           assoc.ackedBytes);
+      recordScalar("Throughput [bit/s]",    assoc.throughput);
+      recordScalar("Transmitted Bytes",     assoc.transmittedBytes);
+      recordScalar("Packets Received",      numPacketsReceived);
+      recordScalar("Packets Dropped",       numPacketsDropped);
+      recordScalar("Fast RTX",              assoc.numFastRtx);
+      recordScalar("Timer-Based RTX",       assoc.numT3Rtx);
+      recordScalar("Sum of R Gap Ranges",   assoc.sumRGapRanges);
+      recordScalar("Sum of NR Gap Ranges",  assoc.sumNRGapRanges);
+      recordScalar("Duplicate Acks",        assoc.numDups);
+      recordScalar("Overfull SACKs",        assoc.numOverfullSACKs);
+      recordScalar("Drops Because New TSN Greater Than Highest TSN", assoc.numDropsBecauseNewTSNGreaterThanHighestTSN);
+      recordScalar("Drops Because No Room In Buffer",                assoc.numDropsBecauseNoRoomInBuffer);
+      recordScalar("Chunks Reneged",                                 assoc.numChunksReneged);
+      recordScalar("sackPeriod", (simtime_t)par("sackPeriod"));
+      if ((double)par("fairStart") > 0) {
+         recordScalar("fair acked bytes",assoc.fairAckedBytes);
+         recordScalar("fair start time",assoc.fairStart);
+         recordScalar("fair stop time",assoc.fairStop);
+         recordScalar("fair lifetime", assoc.fairLifeTime);
+         recordScalar("fair throughput", assoc.fairThroughput);
+      }
+      recordScalar("Number of PacketDrop Reports", numPktDropReports);
+
+      if ((assoc.cumEndToEndDelay / assoc.numEndToEndMessages) > 0) {
+         uint32 msgnum = assoc.numEndToEndMessages - assoc.startEndToEndDelay;
+         if (assoc.stopEndToEndDelay > 0)
+             msgnum -= (assoc.numEndToEndMessages - assoc.stopEndToEndDelay);
+         recordScalar("Average End to End Delay", assoc.cumEndToEndDelay / msgnum);
+      }
+
+      recordScalar("RTXMethod", (double)par("RTXMethod"));
+#ifdef HAVE_GETTIMEOFDAY
+      const unsigned long long shutdownTime = getMicroTime();
+      recordScalar("Real Time Consumed", (double)(shutdownTime - StartupTime) / 1000000.0);
+#endif
+   }
 }
 
 
-
-void SCTP::removeAssociation(SCTPAssociation *conn)
+const char* SCTP::intToChunk(const uint32 type)
 {
-    bool            ok    = false;
-    bool            find = false;
-    const int32 id    = conn->assocId;
-
-    sctpEV3 << "Deleting SCTP connection " << conn << " id= "<< id << endl;
-
-    printInfoConnMap();
-    if (sizeConnMap > 0) {
-        AssocStatMap::iterator assocStatMapIterator = assocStatMap.find(conn->assocId);
-        if (assocStatMapIterator != assocStatMap.end()) {
-            assocStatMapIterator->second.stop        = simulation.getSimTime();
-            assocStatMapIterator->second.lifeTime    = assocStatMapIterator->second.stop - assocStatMapIterator->second.start;
-            assocStatMapIterator->second.throughput = assocStatMapIterator->second.ackedBytes*8 / assocStatMapIterator->second.lifeTime.dbl();
-        }
-        while (!ok) {
-            if (sizeConnMap == 0) {
-                ok = true;
-            }
-            else {
-                for (SctpConnMap::iterator sctpConnMapIterator = sctpConnMap.begin();
-                      sctpConnMapIterator != sctpConnMap.end(); sctpConnMapIterator++) {
-                    if (sctpConnMapIterator->second != NULL) {
-                        SCTPAssociation* assoc = sctpConnMapIterator->second;
-                        if (assoc->assocId == conn->assocId) {
-                            if (assoc->T1_InitTimer) {
-                                assoc->stopTimer(assoc->T1_InitTimer);
-                            }
-                            if (assoc->T2_ShutdownTimer) {
-                                assoc->stopTimer(assoc->T2_ShutdownTimer);
-                            }
-                            if (assoc->T5_ShutdownGuardTimer) {
-                                assoc->stopTimer(assoc->T5_ShutdownGuardTimer);
-                            }
-                            if (assoc->SackTimer) {
-                                assoc->stopTimer(assoc->SackTimer);
-                            }
-                            sctpConnMap.erase(sctpConnMapIterator);
-                            sizeConnMap--;
-                            find = true;
-                            break;
-                        }
-                    }
-                }
-            }
-
-            if (!find) {
-                ok = true;
-            }
-            else {
-                find = false;
-            }
-        }
-    }
-
-    // T.D. 26.11.09: Write statistics
-    char str[128];
-    for (SCTPAssociation::SCTPPathMap::iterator pathMapIterator = conn->sctpPathMap.begin();
-          pathMapIterator != conn->sctpPathMap.end(); pathMapIterator++) {
-        const SCTPPathVariables* path = pathMapIterator->second;
-        snprintf((char*)&str, sizeof(str), "Number of Fast Retransmissions %d:%s",
-                 conn->assocId, path->remoteAddress.str().c_str());
-        recordScalar(str, path->numberOfFastRetransmissions);
-        snprintf((char*)&str, sizeof(str), "Number of Timer-Based Retransmissions %d:%s",
-                 conn->assocId, path->remoteAddress.str().c_str());
-        recordScalar(str, path->numberOfTimerBasedRetransmissions);
-        snprintf((char*)&str, sizeof(str), "Number of Heartbeats Sent %d:%s",
-                 conn->assocId, path->remoteAddress.str().c_str());
-        recordScalar(str, path->numberOfHeartbeatsSent);
-        snprintf((char*)&str, sizeof(str), "Number of Heartbeats Received %d:%s",
-                 conn->assocId, path->remoteAddress.str().c_str());
-        recordScalar(str, path->numberOfHeartbeatsRcvd);
-        snprintf((char*)&str, sizeof(str), "Number of Heartbeat ACKs Sent %d:%s",
-                 conn->assocId, path->remoteAddress.str().c_str());
-        recordScalar(str, path->numberOfHeartbeatAcksSent);
-        snprintf((char*)&str, sizeof(str), "Number of Heartbeat ACKs Received %d:%s",
-                 conn->assocId, path->remoteAddress.str().c_str());
-        recordScalar(str, path->numberOfHeartbeatAcksRcvd);
-    }
-
-
-    conn->removePath();
-    conn->deleteStreams();
-
-    // TD 20.11.09: Chunks may be in the transmission and retransmission queues simultaneously.
-    //                   Remove entry from transmission queue if it is already in the retransmission queue.
-    for (SCTPQueue::PayloadQueue::iterator i = conn->getRetransmissionQueue()->payloadQueue.begin();
-          i != conn->getRetransmissionQueue()->payloadQueue.end(); i++) {
-        SCTPQueue::PayloadQueue::iterator j = conn->getTransmissionQueue()->payloadQueue.find(i->second->tsn);
-        if(j != conn->getTransmissionQueue()->payloadQueue.end()) {
-            conn->getTransmissionQueue()->payloadQueue.erase(j);
-        }
-    }
-     // TD 20.11.09: Now, both queues can be safely deleted.
-    delete conn->getRetransmissionQueue();
-    delete conn->getTransmissionQueue();
-
-    AppConnKey key;
-    key.appGateIndex = conn->appGateIndex;
-    key.assocId       = conn->assocId;
-    sctpAppConnMap.erase(key);
-    assocList.remove(conn);
-    delete conn;
+   switch (type)
+   {
+      case   0: return "DATA";
+      case   1: return "INIT";
+      case   2: return "INIT_ACK";
+      case   3: return "SACK";
+      case   4: return "HEARTBEAT";
+      case   5: return "HEARTBEAT_ACK";
+      case   6: return "ABORT";
+      case   7: return "SHUTDOWN";
+      case   8: return "SHUTDOWN_ACK";
+      case   9: return "ERRORTYPE";
+      case  10: return "COOKIE_ECHO";
+      case  11: return "COOKIE_ACK";
+      case  14: return "SHUTDOWN_COMPLETE";
+      case  15: return "AUTH";
+      case  16: return "NR-SACK";
+      case 128: return "ASCONF_ACK";
+      case 129: return "PKTDROP";
+      case 130: return "STREAM_RESET";
+      case 192: return "FORWARD_TSN";
+      case 193: return "ASCONF";
+   }
+   return("???");
 }
 
-SCTPAssociation* SCTP::getAssoc(int32 assocId)
+uint32 SCTP::chunkToInt(const char* type)
 {
-    for (SctpAppConnMap::iterator i = sctpAppConnMap.begin(); i!=sctpAppConnMap.end(); i++)
-    {
-        if (i->first.assocId==assocId)
-            return i->second;
-    }
-    return NULL;
+   if (strcmp(type, "DATA")==0)              return 0;
+   if (strcmp(type, "INIT")==0)              return 1;
+   if (strcmp(type, "INIT_ACK")==0)          return 2;
+   if (strcmp(type, "SACK")==0)              return 3;
+   if (strcmp(type, "HEARTBEAT")==0)         return 4;
+   if (strcmp(type, "HEARTBEAT_ACK")==0)     return 5;
+   if (strcmp(type, "ABORT")==0)             return 6;
+   if (strcmp(type, "SHUTDOWN")==0)          return 7;
+   if (strcmp(type, "SHUTDOWN_ACK")==0)      return 8;
+   if (strcmp(type, "ERRORTYPE")==0)         return 9;
+   if (strcmp(type, "COOKIE_ECHO")==0)       return 10;
+   if (strcmp(type, "COOKIE_ACK")==0)        return 11;
+   if (strcmp(type, "SHUTDOWN_COMPLETE")==0) return 14;
+   if (strcmp(type, "AUTH")==0)              return 15;
+   if (strcmp(type, "NR-SACK")==0)           return 16;
+   if (strcmp(type, "ASCONF_ACK")==0)        return 128;
+   if (strcmp(type, "PKTDROP")==0)           return 129;
+   if (strcmp(type, "STREAM_RESET")==0)      return 130;
+   if (strcmp(type, "FORWARD_TSN")==0)       return 192;
+   if (strcmp(type, "ASCONF")==0)            return 193;
+   sctpEV3 << "ChunkConversion not successful" << endl;
+   return(0xffffffff);
 }
 
-void SCTP::finish()
+#ifdef HAVE_GETTIMEOFDAY
+unsigned long long SCTP::getMicroTime()
 {
-    SctpConnMap::iterator connMapIterator = sctpConnMap.begin();
-    while (connMapIterator != sctpConnMap.end()) {
-        removeAssociation(connMapIterator->second);
-        connMapIterator = sctpConnMap.begin();
-    }
-    ev << getFullPath() << ": finishing SCTP with "
-        << sctpConnMap.size() << " connections open." << endl;
-
-    for (AssocStatMap::const_iterator iterator = assocStatMap.begin();
-          iterator != assocStatMap.end(); iterator++) {
-        const SCTP::AssocStat& assoc = iterator->second;
-
-        ev << "Association " << assoc.assocId << ": started at " << assoc.start
-            << " and finished at " << assoc.stop << " --> lifetime: " << assoc.lifeTime << endl;
-        ev << "Association " << assoc.assocId << ": sent bytes=" << assoc.sentBytes
-            << ", acked bytes=" << assoc.ackedBytes<< ", throughput=" << assoc.throughput<< " bit/s" << endl;
-        ev << "Association " << assoc.assocId << ": transmitted Bytes="
-            << assoc.transmittedBytes<< ", retransmitted Bytes=" << assoc.transmittedBytes-assoc.ackedBytes<< endl;
-        ev << "Association " << assoc.assocId << ": number of Fast RTX="
-            << assoc.numFastRtx << ", number of Timer-Based RTX=" << assoc.numT3Rtx
-            << ", path failures=" << assoc.numPathFailures<< ", ForwardTsns=" << assoc.numForwardTsn<< endl;
-        ev << "AllMessages=" <<numPacketsReceived<< " BadMessages=" <<numPacketsDropped<< endl;
-
-        recordScalar("Association Lifetime", assoc.lifeTime);
-        recordScalar("Acked Bytes",          assoc.ackedBytes);
-        recordScalar("Throughput [bit/s]",   assoc.throughput);
-        recordScalar("Transmitted Bytes",    assoc.transmittedBytes);
-        recordScalar("Fast RTX",                 assoc.numFastRtx);
-        recordScalar("Timer-Based RTX",      assoc.numT3Rtx);
-        recordScalar("Duplicate Acks",       assoc.numDups);
-        recordScalar("Packets Received",         numPacketsReceived);
-        recordScalar("Packets Dropped",      numPacketsDropped);
-
-    }
+  struct timeval tv;
+  gettimeofday(&tv,NULL);
+  return(((unsigned long long)tv.tv_sec * (unsigned long long)1000000) +
+         (unsigned long long)tv.tv_usec);
 }
+#endif
diff --git a/src/transport/sctp/SCTP.h b/src/transport/sctp/SCTP.h
index 67dab26..72ddccd 100644
--- a/src/transport/sctp/SCTP.h
+++ b/src/transport/sctp/SCTP.h
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2009 Thomas Dreibholz
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -28,38 +28,45 @@
 #include "IPvXAddress.h"
 #include "UDPSocket.h"
 
+#if defined(__LINUX__) || defined(__linux__) || defined(__linux) || defined(__FreeBSD__) || (__APPLE__)
+#define HAVE_GETTIMEOFDAY
+#include <sys/time.h>
+#include <time.h>
+#endif
 
-class SCTPAssociation;
-class SCTPMessage;
+#define sctpEV3 (!SCTP::testing==true)?std::cerr:std::cerr
 
 
-#define sctpEV3 (!SCTP::testing==true)?std::cerr:std::cerr
+#define SCTP_OVER_UDP_UDPPORT 9899
+
 
+class SCTPAssociation;
+class SCTPMessage;
 
 
 /**
  * Implements the SCTP protocol. This section describes the internal
  * architecture of the SCTP model.
  *
- *   You may want to check the SCTPSocket
+ *  You may want to check the SCTPSocket
  * class which makes it easier to use SCTP from applications.
  *
  * The SCTP protocol implementation is composed of several classes (discussion
  * follows below):
- *   - SCTP: the module class
- *   - SCTPAssociation: manages a connection
- *   - SCTPSendQueue, SCTPReceiveQueue: abstract base classes for various types
- *      of send and receive queues
- *   - SCTPAlgorithm: abstract base class for SCTP algorithms
+ *  - SCTP: the module class
+ *  - SCTPAssociation: manages an association
+ *  - SCTPSendQueue, SCTPReceiveQueue: abstract base classes for various types
+ *    of send and receive queues
+ *  - SCTPAlgorithm: abstract base class for SCTP algorithms
  *
- * SCTP subclassed from cSimpleModule. It manages socketpair-to-connection
+ * SCTP subclassed from cSimpleModule. It manages socketpair-to-association
  * mapping, and dispatches segments and user commands to the appropriate
  * SCTPAssociation object.
  *
- * SCTPAssociation manages the connection, with the help of other objects.
+ * SCTPAssociation manages the association, with the help of other objects.
  * SCTPAssociation itself implements the basic SCTP "machinery": takes care
  * of the state machine, stores the state variables (TCB), sends/receives
- *   etc.
+ *  etc.
  *
  * SCTPAssociation internally relies on 3 objects. The first two are subclassed
  * from SCTPSendQueue and SCTPReceiveQueue. They manage the actual data stream,
@@ -78,166 +85,221 @@ class SCTPMessage;
  * from SCTPAssociation into SCTPAlgorithm: delayed acks, slow start, fast rexmit,
  * etc. are all implemented in SCTPAlgorithm subclasses.
  *
- * The concrete SCTPAlgorithm class to use can be chosen per connection (in OPEN)
+ * The concrete SCTPAlgorithm class to use can be chosen per association (in OPEN)
  * or in a module parameter.
  */
 class INET_API SCTP : public cSimpleModule
 {
-    public:
-        struct AppConnKey
-        {
-            int32 appGateIndex;
-            int32 assocId;
-
-            inline bool operator<(const AppConnKey& b) const
-            {
-                if (appGateIndex!=b.appGateIndex)
-                    return appGateIndex<b.appGateIndex;
-                else
-                    return assocId<b.assocId;
-            }
-
-        };
-        struct SockPair
-        {
-            IPvXAddress localAddr;
-            IPvXAddress remoteAddr;
-            uint16 localPort;
-            uint16 remotePort;
-
-            inline bool operator<(const SockPair& b) const
-            {
-                if (remoteAddr!=b.remoteAddr)
-                    return remoteAddr<b.remoteAddr;
-                else if (localAddr!=b.localAddr)
-                    return localAddr<b.localAddr;
-                else if (remotePort!=b.remotePort)
-                    return remotePort<b.remotePort;
-                else
-                    return localPort<b.localPort;
+   public:
+      struct AppAssocKey
+      {
+         int32 appGateIndex;
+         int32 assocId;
+
+         inline bool operator<(const AppAssocKey& b) const
+         {
+            if (appGateIndex!=b.appGateIndex) {
+               return appGateIndex<b.appGateIndex;
             }
-        };
-        struct VTagPair
-        {
-            uint32 peerVTag;
-            uint32 localVTag;
-            uint16 localPort;
-            uint16 remotePort;
-
-            /*inline bool operator<(const VTagPair& b) const
-            {
-                if (peerVTag!=b.peerVTag)
-                    return peerVTag<b.peerVTag;
-                else if (remotePort!=b.remotePort)
-                    return remotePort<b.remotePort;
-                else
-                    return localPort<b.localPort;
-            }*/
-        };
-        typedef struct
-        {
-            int32 assocId;
-            simtime_t start;
-            simtime_t stop;
-            uint64 rcvdBytes;
-            uint64 sentBytes;
-            uint64 transmittedBytes;
-            uint64 ackedBytes;
-            uint32 numFastRtx;
-            uint32 numDups;
-            uint32 numT3Rtx;
-            uint32 numPathFailures;
-            uint32 numForwardTsn;
-            double throughput;
-            simtime_t lifeTime;
-        }AssocStat;
-
-        typedef std::map<int32,AssocStat> AssocStatMap;
-        AssocStatMap assocStatMap;
-        typedef std::map<int32, VTagPair> SctpVTagMap;
-        SctpVTagMap sctpVTagMap;
-
-
-        typedef std::map<AppConnKey,SCTPAssociation*> SctpAppConnMap;
-        typedef std::map<SockPair,SCTPAssociation*> SctpConnMap;
-
-
-        SctpAppConnMap sctpAppConnMap;
-        SctpConnMap sctpConnMap;
-        std::list<SCTPAssociation*>assocList;
-    protected:
-        int32 sizeConnMap;
-        static int32 nextConnId;
-
-        uint16 nextEphemeralPort;
-
-        SCTPAssociation *findAssocForMessage(IPvXAddress srcAddr, IPvXAddress destAddr, uint32 srcPort, uint32 destPort, bool findListen);
-        SCTPAssociation *findAssocForApp(int32 appGateIndex, int32 assocId);
-        void sendAbortFromMain(SCTPMessage* sctpmsg, IPvXAddress srcAddr, IPvXAddress destAddr);
-        void sendShutdownCompleteFromMain(SCTPMessage* sctpmsg, IPvXAddress srcAddr, IPvXAddress destAddr);
-        void updateDisplayString();
-
-    public:
-        static bool testing;         // switches between sctpEV and testingEV
-        static bool logverbose;  // if !testing, turns on more verbose logging
-        void printInfoConnMap();
-        void printVTagMap();
-
-        void removeAssociation(SCTPAssociation *assoc);
-        simtime_t testTimeout;
-        uint32 numGapReports;
-        uint32 numPacketsReceived;
-        uint32 numPacketsDropped;
-        //double failover();
-    public:
-        //Module_Class_Members(SCTP, cSimpleModule, 0);
-        virtual ~SCTP();
-        virtual void initialize();
-        virtual void handleMessage(cMessage *msg);
-        virtual void finish();
-
-        inline AssocStat* getAssocStat(uint32 assocId) {
-            SCTP::AssocStatMap::iterator found = assocStatMap.find(assocId);
-            if(found != assocStatMap.end()) {
-              return(&found->second);
+            else {
+               return assocId<b.assocId;
             }
-            return(NULL);
-        }
-
-        /**
-        * To be called from SCTPAssociation when socket pair    changes
-        */
-        void updateSockPair(SCTPAssociation *assoc, IPvXAddress localAddr, IPvXAddress remoteAddr, int32 localPort, int32 remotePort);
-        void addLocalAddress(SCTPAssociation *conn, IPvXAddress address);
-        void addLocalAddressToAllRemoteAddresses(SCTPAssociation *conn, IPvXAddress address, std::vector<IPvXAddress> remAddresses);
-        void addRemoteAddress(SCTPAssociation *conn, IPvXAddress localAddress, IPvXAddress remoteAddress);
-        void removeLocalAddressFromAllRemoteAddresses(SCTPAssociation *conn, IPvXAddress address, std::vector<IPvXAddress> remAddresses);
-        void removeRemoteAddressFromAllConnections(SCTPAssociation *conn, IPvXAddress address, std::vector<IPvXAddress> locAddresses);
-        /**
-        * Update assocs socket pair, and register newAssoc (which'll keep LISTENing).
-        * Also, assoc will get a new assocId (and newAssoc will live on with its old assocId).
-        */
-        void addForkedAssociation(SCTPAssociation *assoc, SCTPAssociation *newAssoc, IPvXAddress localAddr, IPvXAddress remoteAddr, int32 localPort, int32 remotePort);
-
-        /**
-        * To be called from SCTPAssociation: reserves an ephemeral port for the connection.
-        */
-        uint16 getEphemeralPort();
-
-        /**
-        * Generates a new integer, to be used as assocId. (assocId is part of the key
-        * which associates connections with their apps).
-        */
-        static int32 getNewConnId() {return ++nextConnId;}
-
-        SCTPAssociation* getAssoc(int32 assocId);
-        SCTPAssociation *findAssocWithVTag(uint32 peerVTag, uint32 remotePort, uint32 localPort);
-        SctpVTagMap getVTagMap() {return sctpVTagMap;};
-
-        void bindPortForUDP();
+         }
+      };
+
+      struct SockPair
+      {
+         IPvXAddress localAddr;
+         IPvXAddress remoteAddr;
+         uint16      localPort;
+         uint16      remotePort;
+
+         inline bool operator<(const SockPair& b) const
+         {
+            if (remoteAddr!=b.remoteAddr)
+               return remoteAddr<b.remoteAddr;
+            else if (localAddr!=b.localAddr)
+               return localAddr<b.localAddr;
+            else if (remotePort!=b.remotePort)
+               return remotePort<b.remotePort;
+            else
+               return localPort<b.localPort;
+         }
+      };
+
+      struct VTagPair
+      {
+         uint32 peerVTag;
+         uint32 localVTag;
+         uint16 localPort;
+         uint16 remotePort;
+
+         /*inline bool operator<(const VTagPair& b) const
+         {
+            if (peerVTag!=b.peerVTag)
+               return peerVTag<b.peerVTag;
+            else if (remotePort!=b.remotePort)
+               return remotePort<b.remotePort;
+            else
+               return localPort<b.localPort;
+         }*/
+      };
+      typedef struct
+      {
+         int32     assocId;
+         simtime_t start;
+         simtime_t stop;
+         uint64    rcvdBytes;
+         uint64    sentBytes;
+         uint64    transmittedBytes;
+         uint64    ackedBytes;
+         uint32    numFastRtx;
+         uint32    numDups;
+         uint32    numT3Rtx;
+         uint32    numPathFailures;
+         uint32    numForwardTsn;
+         uint32    numOverfullSACKs;
+         uint64    sumRGapRanges;                              // T.D. 25.01.2011: Total sum of RGap ranges (Last RGapStop - CumAck)
+         uint64    sumNRGapRanges;                             // T.D. 25.01.2011: Total sum of NRGap ranges (Last NRGapStop - CumAck)
+         uint32    numDropsBecauseNewTSNGreaterThanHighestTSN;
+         uint32    numDropsBecauseNoRoomInBuffer;
+         uint32    numChunksReneged;
+         double    throughput;
+         simtime_t lifeTime;
+         simtime_t fairStart;
+         simtime_t fairStop;
+         uint64    fairAckedBytes;
+         double    fairThroughput;
+         simtime_t fairLifeTime;
+         uint64    numEndToEndMessages;
+         SimTime   cumEndToEndDelay;
+         uint64    startEndToEndDelay;
+         uint64    stopEndToEndDelay;
+      } AssocStat;
+
+      typedef std::map<int32,AssocStat>              AssocStatMap;
+      typedef std::map<int32, VTagPair>              SctpVTagMap;
+      typedef std::map<AppAssocKey,SCTPAssociation*> SctpAppAssocMap;
+      typedef std::map<SockPair,SCTPAssociation*>    SctpAssocMap;
+
+      AssocStatMap                                   assocStatMap;
+      SctpVTagMap                                    sctpVTagMap;
+      SctpAppAssocMap                                sctpAppAssocMap;
+      SctpAssocMap                                   sctpAssocMap;
+      std::list<SCTPAssociation*>                    assocList;
+
+   protected:
+      int32        sizeAssocMap;
+      uint16       nextEphemeralPort;
+      static int32 nextAssocId;
+
+      SCTPAssociation* findAssocForMessage(const IPvXAddress& srcAddr,
+                                           const IPvXAddress& destAddr,
+                                           const uint32       srcPort,
+                                           const uint32       destPort,
+                                           const bool         findListen);
+      SCTPAssociation* findAssocForApp(const int32 appGateIndex,
+                                       const int32 assocId);
+      void sendAbortFromMain(SCTPMessage*       sctpmsg,
+                             const IPvXAddress& srcAddr,
+                             const IPvXAddress& destAddr);
+      void sendShutdownCompleteFromMain(SCTPMessage* sctpmsg,
+                                        const IPvXAddress& srcAddr,
+                                        const IPvXAddress& destAddr);
+      void updateDisplayString();
+
+   public:
+      static bool testing;     // switches between sctpEV and testingEV
+      static bool checkQueues; // perform intensive checks for queue lengths for debugging
+      static bool logverbose;  // if !testing, turns on more verbose logging
+      void printInfoAssocMap();
+      void printVTagMap();
+      void removeAssociation(SCTPAssociation* assoc);
+
+      simtime_t testTimeout;
+      uint32    numGapReports;
+      uint32    numPacketsReceived;
+      uint32    numPacketsDropped;
+      bool      auth;
+      bool      addIP;
+      bool      pktdrop;
+      bool      sackNow;
+      uint64    numPktDropReports;
+
+   public:
+      virtual ~SCTP();
+      virtual void initialize();
+      virtual void handleMessage(cMessage *msg);
+      virtual void finish();
+
+      inline AssocStat* getAssocStat(const uint32 assocId) {
+         SCTP::AssocStatMap::iterator found = assocStatMap.find(assocId);
+         if(found != assocStatMap.end()) {
+           return(&found->second);
+         }
+         return(NULL);
+      }
+
+      /**
+      * To be called from SCTPAssociation when socket pair  changes
+      */
+      void updateSockPair(SCTPAssociation*   assoc,
+                          const IPvXAddress& localAddr,
+                          const IPvXAddress& remoteAddr,
+                          const int32        localPort,
+                          const int32        remotePort);
+      void addLocalAddress(SCTPAssociation*   assoc,
+                           const IPvXAddress& address);
+      void addLocalAddressToAllRemoteAddresses(SCTPAssociation*                assoc,
+                                               const IPvXAddress&              address,
+                                               const std::vector<IPvXAddress>& remAddresses);
+      void addRemoteAddress(SCTPAssociation*   assoc,
+                            const IPvXAddress& localAddress,
+                            const IPvXAddress& remoteAddress);
+      void removeLocalAddressFromAllRemoteAddresses(SCTPAssociation*                assoc,
+                                                    const IPvXAddress&              address,
+                                                    const std::vector<IPvXAddress>& remAddresses);
+      void removeRemoteAddressFromAllAssociations(SCTPAssociation*                assoc,
+                                                  const IPvXAddress&              address,
+                                                  const std::vector<IPvXAddress>& locAddresses);
+
+      /**
+      * Update assocs socket pair, and register newAssoc (which'll keep LISTENing).
+      * Also, assoc will get a new assocId (and newAssoc will live on with its old assocId).
+      */
+      void addForkedAssociation(SCTPAssociation*   assoc,
+                                SCTPAssociation*   newAssoc,
+                                const IPvXAddress& localAddr,
+                                const IPvXAddress& remoteAddr,
+                                const int32        localPort,
+                                const int32        remotePort);
+
+      /**
+      * To be called from SCTPAssociation: reserves an ephemeral port for the association.
+      */
+      int16 getEphemeralPort();
+
+      /**
+      * Generates a new integer, to be used as assocId. (assocId is part of the key
+      * which associates associations with their apps).
+      */
+      inline static int32 getNewAssocId() { return ++nextAssocId; }
+
+      SCTPAssociation* getAssoc(const int32 assocId);
+      SCTPAssociation* findAssocWithVTag(const uint32 peerVTag,
+                                         const uint32 remotePort,
+                                         const uint32 localPort);
+      inline SctpVTagMap getVTagMap() { return sctpVTagMap; }
+
+      void bindPortForUDP();
+
+      static uint32      chunkToInt(const char* type);
+      static const char* intToChunk(const uint32 type);
+
+#ifdef HAVE_GETTIMEOFDAY
+      static unsigned long long getMicroTime();
+      unsigned long long StartupTime;
+#endif
 };
 
 #endif
-
-
-
diff --git a/src/transport/sctp/SCTP.ned b/src/transport/sctp/SCTP.ned
index 3bd27ac..841f48a 100644
--- a/src/transport/sctp/SCTP.ned
+++ b/src/transport/sctp/SCTP.ned
@@ -1,6 +1,6 @@
 //
-// Copyright (C) 2005-2010 Irene Ruengeler
-// Copyright (C) 2009-2010 Thomas Dreibholz
+// Copyright (C) 2008 Irene Ruengeler
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -20,56 +20,93 @@ package inet.transport.sctp;
 
 simple SCTP
 {
-        parameters:
-        // ====== SCTP Association Parameters =================================
-        int numGapReports                   = default(3);
-        double rtoInitial @unit(s)          = default(3s);
-        double rtoMin @unit(s)              = default(1s);
-        double rtoMax @unit(s)              = default(60s);
-        double rtoAlpha                     = default(0.125);
-        double rtoBeta                      = default(0.250);
-        int maxBurst                        = default(4);
-        int assocMaxRetrans                 = default(10);
-        int pathMaxRetrans                  = default(5);
-        int maxInitRetrans                  = default(8);
-        double sackPeriod @unit(s)          = default(200ms);
-        int sackFrequency                   = default(2);
-        bool reactivatePrimaryPath          = default(false);
-        int sendQueueLimit                  = default(0);
-        double validCookieLifetime @unit(s) = default(10s);
+      parameters:
+      // ====== SCTP Association Parameters =================================
+      int    numGapReports                = default(3);
+      double rtoInitial @unit(s)          = default(3s);
+      double rtoMin @unit(s)              = default(1s);
+      double rtoMax @unit(s)              = default(60s);
+      double rtoAlpha                     = default(0.125);
+      double rtoBeta                      = default(0.250);
+      int    maxBurst                     = default(4);
+      int    assocMaxRetrans              = default(10);
+      int    pathMaxRetrans               = default(5);
+      int    maxInitRetrans               = default(8);
+      double sackPeriod @unit(s)          = default(200ms);
+      int    sackFrequency                = default(2);
+      bool   reactivatePrimaryPath        = default(false);
+      int    sendQueueLimit               = default(0);
+      double validCookieLifetime @unit(s) = default(10s);
 
-        // ====== Testing =====================================================
+      // ====== Testing =====================================================
+      bool   checkQueues                  = default(true);     // T.D. 20.11.2009
 
-        // ====== Heartbeats ==================================================
-        bool enableHeartbeats                   = default(true);
-        double hbInterval @unit(s)              = default(30s);
+      // ====== Heartbeats ==================================================
+      bool   enableHeartbeats             = default(true);
+      double hbInterval @unit(s)          = default(30s);
+      bool   sendHeartbeatsOnActivePaths  = default(false);
 
-        // ====== Nagle Algorithm =============================================
-        bool nagleEnabled                       = default(true);
-        int naglePoint                          = default(1468);
+      // ====== Nagle Algorithm =============================================
+      bool   nagleEnabled                 = default(true);
+      int    naglePoint                   = default(1468);
 
-        // ====== Congestion Control ==========================================
-        bool fastRecoverySupported              = default(true);
-        string sctpAlgorithmClass               = default("SCTPAlg");
-        int ccModule                            = default(0);           // RFC4960=0
+      // ====== Congestion Control ==========================================
+      int    RTXMethod                    = default(0);        // 0=Once after 3 Sacks; 1=once per RTT; 2=Switch off Fast Retransmit; 3=Always after 3 Sacks
+      bool   fastRecoverySupported        = default(true);
+      string sctpAlgorithmClass           = default("SCTPAlg");
+      int    ccModule                     = default(0);        // RFC4960=0
+      bool   osbWithHeader                = default(false);
 
-        int ssModule                            = default(0);           // ROUND_ROBIN=0
-        int arwnd                               = default(65535);
-        int swsLimit                            = default(3000);        // Limit for SWS
-        bool udpEncapsEnabled                   = default(false);
+      // ====== Other Parameters ============================================
+      bool   natFriendly                  = default(false);
+      int    bytesToAddPerRcvdChunk       = default(0);        // for flowcontrol
+      int    bytesToAddPerPeerChunk       = default(0);        // for flowcontrol
+      bool   tellArwnd                    = default(false);    // for flowcontrol
+      int    messageAcceptLimit           = default(0);        // for flowcontrol
+      double fairStart @unit(s)           = default(0s);
+      double fairStop @unit(s)            = default(0s);
+      int    ssModule                     = default(0);        // ROUND_ROBIN=0
+      string streamsToPaths               = default("");
+      int    arwnd                        = default(65535);
+      int    swsLimit                     = default(3000);     // Limit for SWS
+      int    startEndToEndDelay           = default(0);
+      int    stopEndToEndDelay            = default(10);
+      bool   udpEncapsEnabled             = default(false);
+      double throughputInterval           = default(1);
 
+      // ====== Chunk Authentication ========================================
+      bool   auth                         = default(false);
+      string chunks                       = default("");
+      bool   padding                      = default(false);
 
+      // ====== Add-IP ======================================================
+      bool   addIP                        = default(false);
+      double addTime @unit(s)             = default(0s);
+      string addAddress                   = default("");
+      string addIpType                    = default("0");      // SET_PRIMARY_ADDRESS=49156,ADD_IP_ADDRESS=49153,DELETE_IP_ADDRESS=49154
 
+      // ====== Packet Drop =================================================
+      bool   packetDrop                   = default(false);
 
+      // ====== SACK Now ====================================================
+      bool   sackNow                      = default(false);
 
 
+      // ====== SACK Sequence Numbers =======================================
+      bool   checkSackSeqNumber           = default(false);    // T.D. 23.02.2010
 
+      // ====== NR-SACK =====================================================
+      bool   nrSack                       = default(false);    // T.D. 03.03.2010
+      bool   disableReneging              = default(false);    // T.D. 29.10.2010
 
-    gates:
-        input from_appl[];
-        input from_ip;
-        input from_ipv6;
-        output to_appl[];
-        output to_ip;
-        output to_ipv6;
+
+
+
+   gates:
+      input from_appl[];
+      input from_ip;
+      input from_ipv6;
+      output to_appl[];
+      output to_ip;
+      output to_ipv6;
 }
diff --git a/src/transport/sctp/SCTPAlg.cc b/src/transport/sctp/SCTPAlg.cc
index 7c20817..ccfad8c 100644
--- a/src/transport/sctp/SCTPAlg.cc
+++ b/src/transport/sctp/SCTPAlg.cc
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2010 Thomas Dreibholz
+// Copyright (C) 2010-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -24,27 +24,27 @@ Register_Class(SCTPAlg);
 
 SCTPAlg::SCTPAlg() : SCTPAlgorithm()
 {
-    state = NULL;
+   state = NULL;
 }
 
 SCTPAlg::~SCTPAlg()
 {
-    sctpEV3 << "Destructor SCTPAlg" << endl;
-    // Note: don't delete "state" here, it'll be deleted from SCTPAssociation
+   sctpEV3 << "Destructor SCTPAlg" << endl;
+   // Note: don't delete "state" here, it'll be deleted from SCTPAssociation
 }
 
 SCTPStateVariables *SCTPAlg::createStateVariables()
 {
-    ASSERT(state == NULL);
-    state = new SCTPAlgStateVariables();
-    return(state);
+   ASSERT(state == NULL);
+   state = new SCTPAlgStateVariables();
+   return(state);
 }
 
 void SCTPAlg::established(bool active)
 {
-    if (active) {
-        sctpEV3 << "Completing connection: sending DATA" << endl;
-    }
+   if (active) {
+      sctpEV3 << "Completing connection: sending DATA" << endl;
+   }
 }
 
 void SCTPAlg::connectionClosed()
@@ -53,12 +53,12 @@ void SCTPAlg::connectionClosed()
 
 void SCTPAlg::processTimer(cMessage* timer, SCTPEventCode& event)
 {
-    sctpEV3 << "no extra timers in this SCTP variant" << endl;
+   sctpEV3 << "no extra timers in this SCTP variant" << endl;
 }
 
 void SCTPAlg::sendCommandInvoked(SCTPPathVariables* path)
 {
-        assoc->sendOnPath(path);
+      assoc->sendOnPath(path);
 }
 
 void SCTPAlg::receivedDataAck(uint32)
@@ -67,12 +67,12 @@ void SCTPAlg::receivedDataAck(uint32)
 
 void SCTPAlg::receivedDuplicateAck()
 {
-    sctpEV3 << "Duplicate ACK #" << endl;
+   sctpEV3 << "Duplicate ACK #" << endl;
 }
 
 void SCTPAlg::receivedAckForDataNotYetSent(uint32 seq)
 {
-    sctpEV3 << "ACK acks something not yet sent, sending immediate ACK" << endl;
+   sctpEV3 << "ACK acks something not yet sent, sending immediate ACK" << endl;
 }
 
 void SCTPAlg::sackSent()
diff --git a/src/transport/sctp/SCTPAlg.h b/src/transport/sctp/SCTPAlg.h
index 4035dac..3a9726c 100644
--- a/src/transport/sctp/SCTPAlg.h
+++ b/src/transport/sctp/SCTPAlg.h
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2008 Irene Ruengeler
-// Copyright (C) 2010 Thomas Dreibholz
+// Copyright (C) 2010-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -29,48 +29,48 @@
 class INET_API SCTPAlgStateVariables : public SCTPStateVariables
 {
   public:
-     //...
+    //...
 };
 
 
 class INET_API SCTPAlg : public SCTPAlgorithm
 {
   protected:
-     SCTPAlgStateVariables *state;
+    SCTPAlgStateVariables *state;
 
   public:
-     /**
-      * Ctor.
-      */
-     SCTPAlg();
+    /**
+     * Ctor.
+     */
+    SCTPAlg();
 
-     /**
-      * Virtual dtor.
-      */
-     virtual ~SCTPAlg();
+    /**
+     * Virtual dtor.
+     */
+    virtual ~SCTPAlg();
 
-     /**
-      * Creates and returns a SCTPStateVariables object.
-      */
-     virtual SCTPStateVariables* createStateVariables();
+    /**
+     * Creates and returns a SCTPStateVariables object.
+     */
+    virtual SCTPStateVariables* createStateVariables();
 
-     virtual void established(bool active);
+    virtual void established(bool active);
 
-     virtual void connectionClosed();
+    virtual void connectionClosed();
 
-     virtual void processTimer(cMessage* timer, SCTPEventCode& event);
+    virtual void processTimer(cMessage* timer, SCTPEventCode& event);
 
-     virtual void sendCommandInvoked(SCTPPathVariables* path);
+    virtual void sendCommandInvoked(SCTPPathVariables* path);
 
-     virtual void receivedDataAck(uint32 firstSeqAcked);
+    virtual void receivedDataAck(uint32 firstSeqAcked);
 
-     virtual void receivedDuplicateAck();
+    virtual void receivedDuplicateAck();
 
-     virtual void receivedAckForDataNotYetSent(uint32 seq);
+    virtual void receivedAckForDataNotYetSent(uint32 seq);
 
-     virtual void sackSent();
+    virtual void sackSent();
 
-     virtual void dataSent(uint32 fromseq);
+    virtual void dataSent(uint32 fromseq);
 
 };
 
diff --git a/src/transport/sctp/SCTPAlgorithm.h b/src/transport/sctp/SCTPAlgorithm.h
index 39f132a..67de7c9 100644
--- a/src/transport/sctp/SCTPAlgorithm.h
+++ b/src/transport/sctp/SCTPAlgorithm.h
@@ -23,7 +23,6 @@
 #include "SCTPQueue.h"
 
 
-
 /**
  * Abstract base class for SCTP algorithms which encapsulate all behaviour
  * during data transfer state: flavour of congestion control, fast
@@ -79,5 +78,3 @@ class INET_API SCTPAlgorithm : public cPolymorphic
 };
 
 #endif
-
-
diff --git a/src/transport/sctp/SCTPAssociation.h b/src/transport/sctp/SCTPAssociation.h
index fae2b6e..700d7b9 100644
--- a/src/transport/sctp/SCTPAssociation.h
+++ b/src/transport/sctp/SCTPAssociation.h
@@ -1,6 +1,6 @@
 //
-// Copyright (C) 2005-2010 Irene Ruengeler
-// Copyright (C) 2009-2010 Thomas Dreibholz
+// Copyright (C) 2008 Irene Ruengeler
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -27,6 +27,10 @@
 #include "RoutingTableAccess.h"
 #include "InterfaceTable.h"
 #include "InterfaceTableAccess.h"
+#include "TimeStatsCollector.h"
+#include "ChunkMap.h"
+#include "GapList.h"
+#include "SCTPSeqNumbers.h"
 #include "SCTPQueue.h"
 #include "SCTPSendStream.h"
 #include "SCTPReceiveStream.h"
@@ -52,14 +56,14 @@ typedef std::vector<IPvXAddress> AddressVector;
 
 enum SctpState
 {
-    SCTP_S_CLOSED                = 0,
-    SCTP_S_COOKIE_WAIT           = FSM_Steady(1),
-    SCTP_S_COOKIE_ECHOED         = FSM_Steady(2),
-    SCTP_S_ESTABLISHED          = FSM_Steady(3),
-    SCTP_S_SHUTDOWN_PENDING     = FSM_Steady(4),
-    SCTP_S_SHUTDOWN_SENT        = FSM_Steady(5),
-    SCTP_S_SHUTDOWN_RECEIVED    = FSM_Steady(6),
-    SCTP_S_SHUTDOWN_ACK_SENT    = FSM_Steady(7)
+   SCTP_S_CLOSED            = 0,
+   SCTP_S_COOKIE_WAIT       = FSM_Steady(1),
+   SCTP_S_COOKIE_ECHOED     = FSM_Steady(2),
+   SCTP_S_ESTABLISHED       = FSM_Steady(3),
+   SCTP_S_SHUTDOWN_PENDING  = FSM_Steady(4),
+   SCTP_S_SHUTDOWN_SENT     = FSM_Steady(5),
+   SCTP_S_SHUTDOWN_RECEIVED = FSM_Steady(6),
+   SCTP_S_SHUTDOWN_ACK_SENT = FSM_Steady(7)
 };
 
 
@@ -69,125 +73,200 @@ enum SctpState
 //
 enum SCTPEventCode
 {
-    SCTP_E_ASSOCIATE,
-    SCTP_E_OPEN_PASSIVE,
-    SCTP_E_ABORT,
-    SCTP_E_SHUTDOWN,
-    SCTP_E_CLOSE,
-    SCTP_E_SEND,
-    SCTP_E_RCV_INIT,
-    SCTP_E_RCV_ABORT,
-    SCTP_E_RCV_VALID_COOKIE_ECHO,
-    SCTP_E_RCV_INIT_ACK,
-    SCTP_E_RCV_COOKIE_ACK,
-    SCTP_E_RCV_SHUTDOWN,
-    SCTP_E_RCV_SHUTDOWN_ACK,
-    SCTP_E_RCV_SHUTDOWN_COMPLETE,
-    SCTP_E_NO_MORE_OUTSTANDING,
-    SCTP_E_TIMEOUT_INIT_TIMER,
-    SCTP_E_TIMEOUT_SHUTDOWN_TIMER,
-    SCTP_E_TIMEOUT_RTX_TIMER,
-    SCTP_E_TIMEOUT_HEARTBEAT_TIMER,
-    SCTP_E_IGNORE,
-    SCTP_E_RECEIVE,
-    SCTP_E_DUP_RECEIVED,
-    SCTP_E_PRIMARY,
-    SCTP_E_DELIVERED,
-    SCTP_E_QUEUE_MSGS_LIMIT,
-    SCTP_E_QUEUE_BYTES_LIMIT,
-    SCTP_E_SEND_QUEUE_LIMIT,
-    SCTP_E_SEND_SHUTDOWN_ACK,
-    SCTP_E_STOP_SENDING
+   SCTP_E_ASSOCIATE,
+   SCTP_E_OPEN_PASSIVE,
+   SCTP_E_ABORT,
+   SCTP_E_SHUTDOWN,
+   SCTP_E_CLOSE,
+   SCTP_E_SEND,
+   SCTP_E_RCV_INIT,
+   SCTP_E_RCV_ABORT,
+   SCTP_E_RCV_VALID_COOKIE_ECHO,
+   SCTP_E_RCV_INIT_ACK,
+   SCTP_E_RCV_COOKIE_ACK,
+   SCTP_E_RCV_SHUTDOWN,
+   SCTP_E_RCV_SHUTDOWN_ACK,
+   SCTP_E_RCV_SHUTDOWN_COMPLETE,
+   SCTP_E_NO_MORE_OUTSTANDING,
+   SCTP_E_TIMEOUT_INIT_TIMER,
+   SCTP_E_TIMEOUT_SHUTDOWN_TIMER,
+   SCTP_E_TIMEOUT_RTX_TIMER,
+   SCTP_E_TIMEOUT_HEARTBEAT_TIMER,
+   SCTP_E_IGNORE,
+   SCTP_E_RECEIVE,
+   SCTP_E_DUP_RECEIVED,
+   SCTP_E_PRIMARY,
+   SCTP_E_DELIVERED,
+   SCTP_E_QUEUE_MSGS_LIMIT,
+   SCTP_E_QUEUE_BYTES_LIMIT,
+   SCTP_E_SEND_QUEUE_LIMIT,
+   SCTP_E_SEND_SHUTDOWN_ACK,
+   SCTP_E_STOP_SENDING,
+   SCTP_E_STREAM_RESET,
+   SCTP_E_SEND_ASCONF,
+   SCTP_E_SET_STREAM_PRIO
 };
 
 enum SCTPChunkTypes
 {
-    DATA                = 0,
-    INIT                = 1,
-    INIT_ACK            = 2,
-    SACK                = 3,
-    HEARTBEAT           = 4,
-    HEARTBEAT_ACK       = 5,
-    ABORT               = 6,
-    SHUTDOWN            = 7,
-    SHUTDOWN_ACK        = 8,
-    ERRORTYPE           = 9,
-    COOKIE_ECHO         = 10,
-    COOKIE_ACK          = 11,
-    SHUTDOWN_COMPLETE   = 14,
-
+   DATA              = 0,
+   INIT              = 1,
+   INIT_ACK          = 2,
+   SACK              = 3,
+   HEARTBEAT         = 4,
+   HEARTBEAT_ACK     = 5,
+   ABORT             = 6,
+   SHUTDOWN          = 7,
+   SHUTDOWN_ACK      = 8,
+   ERRORTYPE         = 9,
+   COOKIE_ECHO       = 10,
+   COOKIE_ACK        = 11,
+   SHUTDOWN_COMPLETE = 14,
+   FORWARD_TSN       = 192,
+   STREAM_RESET      = 130,
+   ASCONF            = 193,
+   ASCONF_ACK        = 128,
+   AUTH              = 15,
+   NR_SACK           = 16,
+   PKTDROP           = 129,
 };
 
 
 enum SCTPFlags
 {
-    COMPLETE_MESG_UNORDERED = 1,
-    COMPLETE_MESG_ORDERED   = 0
+   COMPLETE_MESG_UNORDERED = 1,
+   COMPLETE_MESG_ORDERED   = 0
+};
+
+enum SCTPPrMethods
+{
+   PR_NONE   = 0,
+   PR_TTL    = 1,
+   PR_RTX    = 2,
+   PR_PRIO   = 3,
+   PR_STRRST = 4
 };
 
+enum SCTPStreamResetConstants
+{
+   NOTHING_TO_DO       = 0,
+   PERFORMED           = 1,
+   DENIED              = 2,
+   WRONG_SSN           = 3,
+   REQUEST_IN_PROGRESS = 4,
+   NO_RESET            = 5,
+   RESET_OUTGOING      = 6,
+   RESET_INCOMING      = 7,
+   RESET_BOTH          = 8,
+   SSN_TSN             = 9,
+   OUTGOING_RESET_REQUEST_PARAMETER = 13,
+   INCOMING_RESET_REQUEST_PARAMETER = 14,
+   SSN_TSN_RESET_REQUEST_PARAMETER  = 15,
+   STREAM_RESET_RESPONSE_PARAMETER  = 16
+};
+
+enum SCTPAddIPCorrelatedTypes
+{
+   SET_PRIMARY_ADDRESS          = 49156,
+   ADAPTATION_LAYER_INDICATION  = 49158,
+   SUPPORTED_EXTENSIONS         = 32776,
+   ADD_IP_ADDRESS               = 49153,
+   DELETE_IP_ADDRESS            = 49154,
+   ERROR_CAUSE_INDICATION       = 49155,
+   SUCCESS_INDICATION           = 49157,
+   ERROR_DELETE_LAST_IP_ADDRESS = 256,
+   ERROR_DELETE_SOURCE_ADDRESS  = 258
+};
 
 enum SCTPParameterTypes
 {
-    UNRECOGNIZED_PARAMETER          = 8,
-    SUPPORTED_ADDRESS_TYPES         = 12,
+   UNRECOGNIZED_PARAMETER          = 8,
+   SUPPORTED_ADDRESS_TYPES         = 12,
+   FORWARD_TSN_SUPPORTED_PARAMETER = 49152,
+   RANDOM                          = 32770,
+   CHUNKS                          = 32771,
+   HMAC_ALGO                       = 32772
 };
 
+enum SCTPErrorCauses
+{
+   UNSUPPORTED_HMAC  = 261,
+   MISSING_NAT_ENTRY = 177
+};
 
 
 
 enum SCTPCCModules
 {
-    RFC4960 = 0
+   RFC4960 = 0
 };
 
 enum SCTPStreamSchedulers
 {
-    ROUND_ROBIN             = 0
+   ROUND_ROBIN            = 0,
+   ROUND_ROBIN_PACKET     = 1,
+   RANDOM_SCHEDULE        = 2,
+   RANDOM_SCHEDULE_PACKET = 3,
+   FAIR_BANDWITH          = 4,
+   FAIR_BANDWITH_PACKET   = 5,
+   PRIORITY               = 6,
+   FCFS                   = 7,
+   PATH_MANUAL            = 8,
+   PATH_MAP_TO_PATH       = 9
 };
 
 
-#define SCTP_COMMON_HEADER              12      // without options
-#define SCTP_INIT_CHUNK_LENGTH          20
-#define SCTP_DATA_CHUNK_LENGTH          16
-#define SCTP_SACK_CHUNK_LENGTH          16
+#define SCTP_COMMON_HEADER             12    // without options
+#define SCTP_INIT_CHUNK_LENGTH         20
+#define SCTP_DATA_CHUNK_LENGTH         16
+#define SCTP_SACK_CHUNK_LENGTH         16
+#define SCTP_NRSACK_CHUNK_LENGTH       20
 #define SCTP_HEARTBEAT_CHUNK_LENGTH     4
 #define SCTP_ABORT_CHUNK_LENGTH         4
 #define SCTP_COOKIE_ACK_LENGTH          4
 #define SCTP_FORWARD_TSN_CHUNK_LENGTH   8
 #define SCTP_SHUTDOWN_CHUNK_LENGTH      8
 #define SCTP_SHUTDOWN_ACK_LENGTH        4
-#define SCTP_ERROR_CHUNK_LENGTH         4       // without parameters
-#define IP_HEADER_LENGTH                20
-#define SCTP_DEFAULT_ARWND              (1<<16)
-#define SCTP_DEFAULT_INBOUND_STREAMS    17
-#define SCTP_DEFAULT_OUTBOUND_STREAMS   17
-#define VALID_COOKIE_LIFE_TIME          10
-#define SCTP_COOKIE_LENGTH              76
-#define HB_INTERVAL                     30
-#define PATH_MAX_RETRANS                5
-
-#define SCTP_TIMEOUT_INIT_REXMIT        3     // initially 3 seconds
-#define SCTP_TIMEOUT_INIT_REXMIT_MAX    240   // 4 mins
-#define SACK_DELAY                      0.2
+#define SCTP_ERROR_CHUNK_LENGTH         4    // without parameters
+#define SCTP_STREAM_RESET_CHUNK_LENGTH                  4   // without parameters
+#define SCTP_OUTGOING_RESET_REQUEST_PARAMETER_LENGTH   16   // without streams
+#define SCTP_INCOMING_RESET_REQUEST_PARAMETER_LENGTH    8   // without streams
+#define SCTP_SSN_TSN_RESET_REQUEST_PARAMETER_LENGTH     8
+#define SCTP_STREAM_RESET_RESPONSE_PARAMETER_LENGTH    12
+#define SCTP_ADD_IP_CHUNK_LENGTH                        8
+#define SCTP_ADD_IP_PARAMETER_LENGTH                    8
+#define SCTP_AUTH_CHUNK_LENGTH                          8
+#define SCTP_PKTDROP_CHUNK_LENGTH                      16  // without included dropped packet
+#define SHA_LENGTH                                     20
+#define IP_HEADER_LENGTH                               20
+#define SCTP_DEFAULT_ARWND                        (1<<16)
+#define SCTP_DEFAULT_INBOUND_STREAMS                   17
+#define SCTP_DEFAULT_OUTBOUND_STREAMS                  17
+#define VALID_COOKIE_LIFE_TIME                         10
+#define SCTP_COOKIE_LENGTH                             76
+#define HB_INTERVAL                                    30
+#define PATH_MAX_RETRANS                                5
+
+#define SCTP_TIMEOUT_INIT_REXMIT           3   // initially 3 seconds
+#define SCTP_TIMEOUT_INIT_REXMIT_MAX     240   // 4 mins
+#define SACK_DELAY                       0.2
 #define RTO_BETA                        0.25
-#define RTO_ALPHA                       0.125
-#define RTO_INITIAL                     3
-#define IPTOS_DEFAULT                   0x10      // IPTOS_LOWDELAY
+#define RTO_ALPHA                      0.125
+#define RTO_INITIAL                        3
+#define IPTOS_DEFAULT                   0x10   // IPTOS_LOWDELAY
 
-#define DEFAULT_MAX_SENDQUEUE           0           /* unlimited send queue */
-#define DEFAULT_MAX_RECVQUEUE           0           /* unlimited recv queue - unused really */
+#define DEFAULT_MAX_SENDQUEUE              0       /* unlimited send queue */
+#define DEFAULT_MAX_RECVQUEUE              0       /* unlimited recv queue - unused really */
 
-#define MAX_ASSOCS                      10
+#define MAX_ASSOCS                        10
 
 #define SCTP_MAX_PAYLOAD                1488 // 12 bytes for common header
 
-#define MAX_GAP_COUNT                   500
-#define MAX_GAP_REPORTS                 4
-#define ADD_PADDING(x)                  ((((x) + 3) >> 2) << 2)
+#define ADD_PADDING(x)                     ((((x) + 3) >> 2) << 2)
 
-#define DEBUG                           1
+#define DEBUG                              1
 
-#define SHUTDOWN_GUARD_TIMEOUT          180
+#define SHUTDOWN_GUARD_TIMEOUT           180
 
 /**
  * Returns the minimum of a and b.
@@ -202,704 +281,930 @@ inline double max(const double a, const double b) { return (a < b) ? b : a; }
 
 class INET_API SCTPPathVariables : public cPolymorphic
 {
-    public:
-        SCTPPathVariables(const IPvXAddress& addr, SCTPAssociation* assoc);
-        ~SCTPPathVariables();
-
-        SCTPAssociation*     association;
-        IPvXAddress          remoteAddress;
-
-        // ====== Path Management =============================================
-        bool                activePath;
-        bool                confirmed;
-        bool                requiresRtx;
-        bool                primaryPathCandidate;
-        bool                forceHb;
-        // ====== T3 Timer Handling ===========================================
-        // Set to TRUE when CumAck has acknowledged TSNs on this path.
-        // Needed to reset T3 timer.
-        bool                newCumAck;                              // T.D. 05.12.2009
-        // ====== Path Status =================================================
-        uint32              pathErrorCount;
-        uint32              pathErrorThreshold;
-        uint32              pmtu;
-        // ====== Congestion Control ==========================================
-        uint32              cwnd;
-        uint32              ssthresh;
-        uint32              partialBytesAcked;
-        uint32              queuedBytes;                            // T.D. 19.02.2010
-        uint32              outstandingBytes;
-        // ~~~~~~ Temporary storage for SACK handling ~~~~~~~
-        uint32              outstandingBytesBeforeUpdate;   // T.D. 20.10.2009
-        uint32              newlyAckedBytes;                        // T.D. 20.10.2009
-        // ====== Fast Recovery ===============================================
-        bool                fastRecoveryActive;                 // T.D. 21.11.2009
-        uint32              fastRecoveryExitPoint;              // T.D. 21.11.2009
-        simtime_t           fastRecoveryEnteringTime;           // T.D. 03.12.2009
-        // ====== Lowest TSN (used for triggering T3 RTX Timer Restart) =======
-        bool                findLowestTSN;                      // T.D. 08.12.2009
-        bool                lowestTSNRetransmitted;         // T.D. 08.12.2009
-
-        // ====== Timers ======================================================
-        cMessage*           HeartbeatTimer;
-        cMessage*           HeartbeatIntervalTimer;
-        cMessage*           CwndTimer;
-        cMessage*           T3_RtxTimer;
-
-        // ====== Path Status =================================================
-        simtime_t           heartbeatTimeout;
-        simtime_t           heartbeatIntervalTimeout;
-        simtime_t           rtxTimeout;
-        simtime_t           cwndTimeout;
-        simtime_t           updateTime;
-        simtime_t           lastAckTime;
-        simtime_t           pathRto;
-        simtime_t           srtt;
-        simtime_t           rttvar;
-
-        // ====== Path Statistics =============================================
-        unsigned int        gapAcksInLastSACK;
-        unsigned int        gapNAcksInLastSACK;
-        unsigned int        numberOfDuplicates;
-        unsigned int        numberOfFastRetransmissions;
-        unsigned int        numberOfTimerBasedRetransmissions;
-        unsigned int        numberOfHeartbeatsSent;
-        unsigned int        numberOfHeartbeatAcksSent;
-        unsigned int        numberOfHeartbeatsRcvd;
-        unsigned int        numberOfHeartbeatAcksRcvd;
-
-        // ====== Output Vectors ==============================================
-        cOutVector*         pathTSN;
-        cOutVector*         pathRcvdTSN;
-        cOutVector*         pathHb;
-        cOutVector*         pathRcvdHb;
-        cOutVector*         pathHbAck;
-        cOutVector*         pathRcvdHbAck;
-        cOutVector*         statisticsPathRTO;
-        cOutVector*         statisticsPathRTT;
-        cOutVector*         statisticsPathSSthresh;
-        cOutVector*         statisticsPathCwnd;
+   public:
+      SCTPPathVariables(const IPvXAddress& addr, SCTPAssociation* assoc);
+      ~SCTPPathVariables();
+
+      SCTPAssociation*   association;
+      IPvXAddress        remoteAddress;
+
+      // ====== Path Management =============================================
+      bool               activePath;
+      bool               confirmed;
+      bool               requiresRtx;
+      bool               primaryPathCandidate;
+      bool               forceHb;
+      // ====== Last SACK ===================================================
+      simtime_t          lastSACKSent;                   // T.D. 10.07.2011
+      // ====== T3 Timer Handling ===========================================
+      // Set to TRUE when CumAck has acknowledged TSNs on this path.
+      // Needed to reset T3 timer.
+      bool               newCumAck;                      // T.D. 05.12.2009
+      // ====== Path Status =================================================
+      uint32             pathErrorCount;
+      uint32             pathErrorThreshold;
+      uint32             pmtu;
+      // ====== Congestion Control ==========================================
+      uint32             cwnd;
+      uint32             ssthresh;
+      uint32             partialBytesAcked;
+      uint32             queuedBytes;                    // T.D. 19.02.2010
+      uint32             outstandingBytes;
+      // ~~~~~~ Temporary storage for SACK handling ~~~~~~~
+      uint32             outstandingBytesBeforeUpdate;   // T.D. 20.10.2009
+      uint32             newlyAckedBytes;                // T.D. 20.10.2009
+      // ====== Fast Recovery ===============================================
+      bool               fastRecoveryActive;             // T.D. 21.11.2009
+      uint32             fastRecoveryExitPoint;          // T.D. 21.11.2009
+      simtime_t          fastRecoveryEnteringTime;       // T.D. 03.12.2009
+      // ====== Lowest TSN (used for triggering T3 RTX Timer Restart) =======
+      bool               findLowestTSN;                  // T.D. 08.12.2009
+      bool               lowestTSNRetransmitted;         // T.D. 08.12.2009
+
+      // ====== Timers ======================================================
+      cMessage*           HeartbeatTimer;
+      cMessage*           HeartbeatIntervalTimer;
+      cMessage*           CwndTimer;
+      cMessage*           T3_RtxTimer;
+      cMessage*           BlockingTimer;
+      cMessage*           ResetTimer;
+      cMessage*           AsconfTimer;
+
+      // ====== Path Status =================================================
+      simtime_t           heartbeatTimeout;
+      simtime_t           heartbeatIntervalTimeout;
+      simtime_t           rtxTimeout;
+      simtime_t           cwndTimeout;
+      simtime_t           rttUpdateTime;
+      simtime_t           lastAckTime;
+      simtime_t           pathRto;
+      simtime_t           srtt;
+      simtime_t           rttvar;
+
+      // ====== Path Statistics =============================================
+      unsigned int        gapAckedChunksInLastSACK;        // T.D. 18.10.2010: Per-path GapAck'ed chunks in last SACK (R+NR)
+      unsigned int        gapNRAckedChunksInLastSACK;      // T.D. 18.10.2010: Per-path GapAck'ed chunks in last SACK (only NR)
+      unsigned int        gapUnackedChunksInLastSACK;      // T.D. 18.10.2010: Per-path Not-GapAck'ed chunks in last SACK (i.e. chunks between GapAcks)
+      unsigned int        numberOfDuplicates;
+      unsigned int        numberOfFastRetransmissions;
+      unsigned int        numberOfTimerBasedRetransmissions;
+
+      // ====== Output Vectors ==============================================
+      cOutVector*         statisticsPathRTO;
+      cOutVector*         statisticsPathRTT;
+      TimeStatsCollector* statisticsPathSSthresh;
+      TimeStatsCollector* statisticsPathCwnd;
+      TimeStatsCollector* statisticsPathOutstandingBytes;
+      TimeStatsCollector* statisticsPathQueuedSentBytes;
+      TimeStatsCollector* statisticsPathSenderBlockingFraction;
+      TimeStatsCollector* statisticsPathReceiverBlockingFraction;
+      TimeStatsCollector* statisticsPathGapAckedChunksInLastSACK;
+      TimeStatsCollector* statisticsPathGapNRAckedChunksInLastSACK;
+      TimeStatsCollector* statisticsPathGapUnackedChunksInLastSACK;
+      TimeStatsCollector* statisticsPathBandwidth;
+      cOutVector*         vectorPathFastRecoveryState;
+      cOutVector*         vectorPathPbAcked;
+      cOutVector*         vectorPathTSNFastRTX;
+      cOutVector*         vectorPathTSNTimerBased;
+      cOutVector*         vectorPathAckedTSNCumAck;
+      cOutVector*         vectorPathAckedTSNGapAck;
+      cOutVector*         vectorPathSentTSN;
+      cOutVector*         vectorPathReceivedTSN;
 };
 
 
 
 class INET_API SCTPDataVariables : public cPolymorphic
 {
-    public:
-        SCTPDataVariables();
-        ~SCTPDataVariables();
-
-        inline void setInitialDestination(SCTPPathVariables* path) {
-            initialDestination = path;
-        }
-        inline const IPvXAddress& getInitialDestination() const {
-            if(initialDestination != NULL) {
-                return(initialDestination->remoteAddress);
-            }
-            return(zeroAddress);
-        }
-        inline SCTPPathVariables* getInitialDestinationPath() const {
-            return(initialDestination);
-        }
-
-        inline void setLastDestination(SCTPPathVariables* path) {
-            lastDestination = path;
-        }
-        inline const IPvXAddress& getLastDestination() const {
-            if(lastDestination != NULL) {
-                return(lastDestination->remoteAddress);
-            }
-            return(zeroAddress);
-        }
-        inline SCTPPathVariables* getLastDestinationPath() const {
-            return(lastDestination);
-        }
-
-        inline void setNextDestination(SCTPPathVariables* path) {
-            nextDestination = path;
-        }
-        inline const IPvXAddress& getNextDestination() const {
-            if(nextDestination != NULL) {
-                return(nextDestination->remoteAddress);
-            }
-            return(zeroAddress);
-        }
-        inline SCTPPathVariables* getNextDestinationPath() const {
-            return(nextDestination);
-        }
-
-        cPacket*            userData;
-        uint32              len;                                 // Different from wire
-        uint32              booksize;
-        uint32              tsn;
-        uint16              sid;
-        uint16              ssn;
-        bool                enqueuedInTransmissionQ;     // In transmissionQ? Otherwise, it is just in retransmissionQ.
-        bool                countsAsOutstanding;         // Is chunk outstanding?
-        bool                hasBeenFastRetransmitted;
-        bool                hasBeenAbandoned;
-        bool                hasBeenReneged;              // Has chunk been reneged?
-        bool                hasBeenAcked;                    // Has chunk been SACK'ed?
-        bool                bbit;
-        bool                ebit;
-        bool                ordered;
-        uint32              ppid;
-        uint32              gapReports;
-        simtime_t           enqueuingTime;
-        simtime_t           sendTime;
-        simtime_t           ackTime;
-        simtime_t           expiryTime;
-        uint32              numberOfRetransmissions;
-        uint32              numberOfTransmissions;
-        uint32              allowedNoRetransmissions;
-
-    public:
-        static const IPvXAddress zeroAddress;
-
-    private:
-        SCTPPathVariables* initialDestination;
-        SCTPPathVariables* lastDestination;
-        SCTPPathVariables* nextDestination;
+   public:
+      SCTPDataVariables();
+      ~SCTPDataVariables();
+
+      inline void setInitialDestination(SCTPPathVariables* path) {
+         initialDestination = path;
+      }
+      inline const IPvXAddress& getInitialDestination() const {
+         if(initialDestination != NULL) {
+            return(initialDestination->remoteAddress);
+         }
+         return(zeroAddress);
+      }
+      inline SCTPPathVariables* getInitialDestinationPath() const {
+         return(initialDestination);
+      }
+
+      inline void setLastDestination(SCTPPathVariables* path) {
+         lastDestination = path;
+      }
+      inline const IPvXAddress& getLastDestination() const {
+         if(lastDestination != NULL) {
+            return(lastDestination->remoteAddress);
+         }
+         return(zeroAddress);
+      }
+      inline SCTPPathVariables* getLastDestinationPath() const {
+         return(lastDestination);
+      }
+
+      inline void setNextDestination(SCTPPathVariables* path) {
+         nextDestination = path;
+      }
+      inline const IPvXAddress& getNextDestination() const {
+         if(nextDestination != NULL) {
+            return(nextDestination->remoteAddress);
+         }
+         return(zeroAddress);
+      }
+      inline SCTPPathVariables* getNextDestinationPath() const {
+         return(nextDestination);
+      }
+
+      // ====== Chunk Data Management =======================================
+      cPacket*           userData;
+      uint32             len;                       // Different from wire
+      uint32             booksize;
+      uint32             tsn;
+      uint16             sid;
+      uint16             ssn;
+      uint32             ppid;
+      uint32             fragments;                 // Number of fragments => TSNs={tsn, ..., tsn+fragments-1}
+      bool               enqueuedInTransmissionQ;   // In transmissionQ? Otherwise, it is just in retransmissionQ.
+      bool               countsAsOutstanding;       // Is chunk outstanding?
+      bool               hasBeenFastRetransmitted;
+      bool               hasBeenAbandoned;
+      bool               hasBeenReneged;            // Has chunk been reneged?
+      bool               hasBeenAcked;              // Has chunk been SACK'ed?
+      bool               hasBeenCountedAsNewlyAcked;// Chunk has been counted as newly SACK'ed
+      bool               bbit;
+      bool               ebit;
+      bool               ibit;
+      bool               ordered;
+
+      // ====== Retransmission Management ===================================
+      uint32             gapReports;
+      simtime_t          enqueuingTime;
+      simtime_t          sendTime;
+      simtime_t          expiryTime;
+      uint32             numberOfRetransmissions;
+      uint32             numberOfTransmissions;
+      uint32             allowedNoRetransmissions;
+
+      // ====== Advanced Chunk Information ==================================
+      SCTPPathVariables* queuedOnPath;              // T.D. 10.02.2010: The path to account this chunk in qCounters.queuedOnPath
+      SCTPPathVariables* ackedOnPath;               // T.D. 24.02.2010: The path this chunk has been acked on
+      bool               hasBeenTimerBasedRtxed;    // T.D. 11.03.2010: Has chunk been timer-based retransmitted?
+      bool               hasBeenMoved;              // T.D. 18.02.2010: Chunk has been moved to solve buffer blocking
+      bool               wasDropped;                // For receiving side of PKTDROP: chunk dropped
+      bool               wasPktDropped;             // Stays true even if the TSN has been transmitted
+      bool               qs;                        // If true, chunk was sent with quickstart
+      uint32             prMethod;
+      uint32             priority;
+      bool               strReset;
+      simtime_t          firstSendTime;
+	   bool               sendForwardIfAbandoned;
+
+   public:
+      static const IPvXAddress zeroAddress;
+
+      // ====== Private Control Information =================================
+   private:
+      SCTPPathVariables* initialDestination;
+      SCTPPathVariables* lastDestination;
+      SCTPPathVariables* nextDestination;
 };
 
 
 
 class INET_API SCTPStateVariables : public cPolymorphic
 {
-    public:
-        SCTPStateVariables();
-        ~SCTPStateVariables();
-    public:
-        inline void setPrimaryPath(SCTPPathVariables* path) {
-            primaryPath = path;
-        }
-        inline const IPvXAddress& getPrimaryPathIndex() const {
-            if(primaryPath != NULL) {
-                return(primaryPath->remoteAddress);
-            }
-            return(SCTPDataVariables::zeroAddress);
-        }
-        inline SCTPPathVariables* getPrimaryPath() const {
-            return(primaryPath);
-        }
-
-        bool                        active;
-        bool                        fork;
-        bool                        ackPointAdvanced;
-        bool                        dataChunkReceived;
-        bool                        initReceived;
-        bool                        cookieEchoReceived;
-        bool                        newChunkReceived;
-        bool                        firstChunkReceived;
-        bool                        swsAvoidanceInvoked;
-        bool                        probingIsAllowed;
-        bool                        zeroWindowProbing;
-        bool                        alwaysBundleSack;
-        bool                        fastRecoverySupported;
-        bool                        nagleEnabled;
-        bool                        sackAllowed;
-        bool                        reactivatePrimaryPath;
-        bool                        resetPending;
-        bool                        stopReceiving;        // incoming data will be discarded
-        bool                        stopOldData;              // data with TSN<peerTsnAfterReset will be discarded
-        bool                        queueUpdate;
-        bool                        firstDataSent;
-        bool                        peerWindowFull;
-        bool                        zeroWindow;
-        bool                        stopSending;              // will be called when SCTP_E_SHUTDOWN arrived
-        bool                        inOut;
-        bool                        noMoreOutstanding;
-        uint32                      numGapReports;
-        IPvXAddress                 initialPrimaryPath;
-        IPvXAddress                 lastDataSourceAddress;
-        AddressVector               localAddresses;
-        std::list<uint32>           dupList;
-        uint32                      errorCount;           // overall error counter
-        uint64                      peerRwnd;
-        uint64                      initialPeerRwnd;
-        uint64                      localRwnd;
-        uint32                      nextTSN;                  // TSN to be sent
-        uint32                      lastTsnAck;           // stored at the sender side; cumTSNAck announced in a SACK
-        uint32                      cTsnAck;                  // will be put in the SACK chunk
-        uint32                      highestTsnReceived;   // will be set when DATA chunk arrived
-        uint32                      highestTsnAcked;
-        uint32                      highestTsnStored;     // used to compare Tsns in makeRoomForTsn
-        uint32                      lastTsnReceived;          // SACK
-        uint32                      lastTSN;                  // my very last TSN to be sent
-        uint32                      ackState;                 // number of packets to be acknowledged
-        uint32                      numGaps;
-        uint32                      gapStartList[MAX_GAP_COUNT];
-        uint32                      gapStopList[MAX_GAP_COUNT];
-        uint64                      outstandingBytes;     // Number of bytes outstanding
-        uint64                      queuedReceivedBytes;  // Number of bytes in receiver queue
-        uint32                      lastStreamScheduled;
-        uint32                      assocPmtu;                // smallest overall path mtu
-        uint32                      msgNum;                   // indicates the sequence number of the message
-        uint64                      bytesRcvd;
-        uint32                      numRequests;
-        uint32                      bytesToRetransmit;
-        uint32                      messagesToPush;
-        int32                       pushMessagesLeft;
-        uint32                      count;
-        uint8                       localTieTag[32];
-        uint8                       peerTieTag[32];
-        uint64                      queuedMessages;       // Messages buffered at the sender side
-        uint32                      messageAcceptLimit;
-        uint32                      queueLimit;
-        uint16                      header;
-        int32                       probingTimeout;
-        std::vector<int32>          numMsgsReq;
-        int32                       cookieLifeTime;
-        /** Counter for init and cookie retransmissions */
-        int16                       initRetransCounter;
-        simtime_t                   initRexmitTimeout;
-        /** pointer to the init chunk data structure (for retransmissions) */
-        SCTPInitChunk*              initChunk;
-        /** pointer to the cookie chunk data structure (for retransmissions) */
-        SCTPCookieEchoChunk*        cookieChunk;
-        /** pointer to the resetChunk (for retransmission) */
-        SCTPShutdownChunk*          shutdownChunk;
-        SCTPShutdownAckChunk*       shutdownAckChunk;
-        SCTPMessage*                sctpmsg;
-        uint64                      sendQueueLimit;
-        uint64                      sendBuffer;
-        bool                        appSendAllowed;
-        simtime_t                   lastSendQueueAbated;
-        uint32                      nextRSid;
-        uint32                      swsLimit;
-        bool                        lastMsgWasFragment;
-        bool                        enableHeartbeats;
-        SCTPMessage*                sctpMsg;
-        uint16                      chunksAdded;
-        uint16                      dataChunksAdded;
-        uint32                      packetBytes;
-        bool                        authAdded;
-        // ====== Max Burst ===================================================
-        uint32                      maxBurst;
-        bool                        ssNextStream;
-        bool                        ssLastDataChunkSizeSet;
-
-    private:
-        SCTPPathVariables*          primaryPath;
+   public:
+      SCTPStateVariables();
+      ~SCTPStateVariables();
+   public:
+      inline void setPrimaryPath(SCTPPathVariables* path) {
+         primaryPath = path;
+      }
+      inline const IPvXAddress& getPrimaryPathIndex() const {
+         if(primaryPath != NULL) {
+            return(primaryPath->remoteAddress);
+         }
+         return(SCTPDataVariables::zeroAddress);
+      }
+      inline SCTPPathVariables* getPrimaryPath() const {
+         return(primaryPath);
+      }
+
+      bool                     active;
+      bool                     fork;
+      bool                     ackPointAdvanced;
+      bool                     dataChunkReceived;
+      bool                     initReceived;
+      bool                     cookieEchoReceived;
+      bool                     newChunkReceived;
+      bool                     firstChunkReceived;
+      bool                     swsAvoidanceInvoked;
+      bool                     probingIsAllowed;
+      bool                     zeroWindowProbing;
+      bool                     alwaysBundleSack;
+      bool                     fastRecoverySupported;
+      bool                     nagleEnabled;
+      bool                     sackAllowed;
+      bool                     reactivatePrimaryPath;
+      bool                     resetPending;
+      bool                     stopReceiving;        // incoming data will be discarded
+      bool                     stopOldData;          // data with TSN<peerTsnAfterReset will be discarded
+      bool                     queueUpdate;
+      bool                     firstDataSent;
+      bool                     peerWindowFull;
+      bool                     zeroWindow;
+      bool                     stopSending;          // will be called when SCTP_E_SHUTDOWN arrived
+      bool                     inOut;
+      bool                     noMoreOutstanding;
+      uint32                   numGapReports;
+      IPvXAddress              initialPrimaryPath;
+      std::list<SCTPPathVariables*> lastDataSourceList;   // DATA chunk sources for new SACK
+      SCTPPathVariables*       lastDataSourcePath;
+      AddressVector            localAddresses;
+      uint32                   errorCount;           // overall error counter
+      uint64                   peerRwnd;
+      uint64                   initialPeerRwnd;
+      uint64                   localRwnd;
+
+      uint32                   nextTSN;              // TSN to be sent
+      uint32                   lastTsnAck;           // stored at the sender side; cumTSNAck announced in a SACK
+      uint32                   highestTsnAcked;
+      uint32                   lastTsnReceived;      // SACK
+      uint32                   lastTSN;              // my very last TSN to be sent
+      uint32                   ackState;             // number of packets to be acknowledged
+
+      GapList                  gapList;              // GapAck list for incoming DATA chunks
+      std::list<uint32>        dupList;              // Duplicates list for incoming DATA chunks
+
+      uint32                   packetsInTotalBurst;
+      simtime_t                lastTransmission;
+
+      uint64                   outstandingBytes;     // Number of bytes outstanding
+      uint64                   queuedSentBytes;      // Number of bytes in sender queue
+      uint64                   queuedDroppableBytes; // Bytes in send queue droppable by PR-SCTP
+      uint64                   queuedReceivedBytes;  // Number of bytes in receiver queue
+      uint32                   lastStreamScheduled;
+      uint32                   assocPmtu;            // smallest overall path mtu
+      uint32                   msgNum;               // indicates the sequence number of the message
+      uint64                   bytesRcvd;
+      uint32                   numRequests;
+      uint32                   bytesToRetransmit;
+      uint32                   messagesToPush;
+      int32                    pushMessagesLeft;
+      uint32                   count;
+      uint8                    localTieTag[32];
+      uint8                    peerTieTag[32];
+      uint64                   queuedMessages;       // Messages buffered at the sender side
+      uint32                   messageAcceptLimit;
+      uint32                   queueLimit;
+      uint16                   header;
+      int32                    probingTimeout;
+      std::vector<int32>       numMsgsReq;
+      int32                    cookieLifeTime;
+      /** Counter for init and cookie retransmissions */
+      int16                    initRetransCounter;
+      simtime_t                initRexmitTimeout;
+      /** pointer to the init chunk data structure (for retransmissions) */
+      SCTPInitChunk*           initChunk;
+      /** pointer to the cookie chunk data structure (for retransmissions) */
+      SCTPCookieEchoChunk*     cookieChunk;
+      /** pointer to the resetChunk (for retransmission) */
+      SCTPShutdownChunk*       shutdownChunk;
+      SCTPShutdownAckChunk*    shutdownAckChunk;
+      SCTPMessage*             sctpmsg;
+      uint64                   sendQueueLimit;
+      uint64	                sendBuffer;
+      bool                     appSendAllowed;
+      simtime_t                lastSendQueueAbated;
+      uint32                   nextRSid;
+      uint32                   swsLimit;
+      bool                     lastMsgWasFragment;
+      bool                     enableHeartbeats;
+      bool                     sendHeartbeatsOnActivePaths;
+      SCTPMessage*             sctpMsg;
+      uint16                   chunksAdded;
+      uint16                   dataChunksAdded;
+      uint32                   packetBytes;
+      bool                     authAdded;
+
+      // ====== NR-SACK =====================================================
+      bool                     nrSack;                       // T.D. 03.03.2010
+      uint32                   gapReportLimit;               // T.D. 29.10.2010
+      enum GLOVariant {
+         GLOV_None       = 0,
+         GLOV_Optimized1 = 1,
+         GLOV_Optimized2 = 2,
+         GLOV_Shrunken   = 3
+      };
+      uint32                   gapListOptimizationVariant;   // T.D. 18.10.2010
+      bool                     smartOverfullSACKHandling;    // T.D. 25.10.2010
+      bool                     disableReneging;              // T.D. 29.10.2010
+
+      // ====== Retransmission Method =======================================
+      uint32                   rtxMethod;
+      // ====== Max Burst ===================================================
+      uint32                   maxBurst;
+
+      // ====== SACK Sequence Number Checker ================================
+      bool                     checkSackSeqNumber;         // Ensure handling SACKs in original sequence
+      uint32                   outgoingSackSeqNum;
+      uint32                   incomingSackSeqNum;
+
+      // ====== Further features ============================================
+      bool                     osbWithHeader;
+      bool                     padding;
+      bool                     streamReset;
+      bool                     peerStreamReset;
+      bool                     pktDropSent;
+      bool                     peerPktDrop;
+      uint32                   advancedPeerAckPoint;    // PR-SCTP
+      uint32                   lastTsnBeforeReset;      // lastTsn announced in OutgoingStreamResetParameter
+      uint32                   streamResetSequenceNumber;
+      uint32                   expectedStreamResetSequenceNumber;
+      uint32                   peerRequestSn;
+      uint32                   inRequestSn;
+      uint32                   peerTsnAfterReset;
+      uint32                   asconfSn;                // own AddIP serial number
+      uint32                   corrIdNum;
+      uint32                   prMethod;
+      uint16                   hmacType;
+      uint16                   numberAsconfReceived;
+      bool                     peerAuth;
+      bool                     auth;
+      bool                     asconfOutstanding;
+      std::vector<uint16>      chunkList;
+      std::vector<uint16>      peerChunkList;
+      uint8                    keyVector[512];
+      uint32                   sizeKeyVector;
+      uint8                    peerKeyVector[512];
+      uint32                   sizePeerKeyVector;
+      uint8                    sharedKey[512];
+      SCTPStreamResetChunk*    resetChunk;
+      SCTPAsconfChunk*         asconfChunk;
+      bool                     peerAllowsChunks;        // Flowcontrol: indicates whether the peer adjusts the window according to a number of messages
+      uint32                   initialPeerMsgRwnd;
+      uint32                   localMsgRwnd;
+      uint32                   peerMsgRwnd;             // Flowcontrol: corresponds to peerRwnd
+      uint32                   bufferedMessages;        // Messages buffered at the receiver side
+      uint32                   outstandingMessages;     // Outstanding messages on the sender side; used for flowControl; including retransmitted messages
+      uint32                   bytesToAddPerRcvdChunk;
+      uint32                   bytesToAddPerPeerChunk;
+      bool                     tellArwnd;
+      bool                     swsMsgInvoked;           // Flowcontrol: corresponds to swsAvoidanceInvoked
+      bool                     ssOneStreamLeft;
+      std::map<uint16,uint32>  ssPriorityMap;
+      std::map<uint16,int32>   ssFairBandwidthMap;
+      std::map<uint16,int32>   ssStreamToPathMap;
+      simtime_t                lastThroughputTime;
+      std::map<uint16,uint32>  streamThroughput;
+      simtime_t                lastAssocThroughputTime;
+      uint32                   assocThroughput;
+      double                   throughputInterval;
+      bool                     ssNextStream;
+      bool                     ssLastDataChunkSizeSet;
+
+   private:
+      SCTPPathVariables*       primaryPath;
 };
 
 
 
 class INET_API SCTPAssociation : public cObject
 {
-    friend class SCTP;
-    friend class SCTPPathVariables;
-
-    // map for storing the path parameters
-    typedef std::map<IPvXAddress,SCTPPathVariables*> SCTPPathMap;
-    // map for storing the queued bytes per path
-    typedef std::map<IPvXAddress, uint32> CounterMap;
-    typedef struct counter {
-        uint64    roomSumSendStreams;
-        uint64    bookedSumSendStreams;
-        uint64    roomSumRcvStreams;
-        CounterMap roomTransQ;
-        CounterMap bookedTransQ;
-        CounterMap roomRetransQ;
-    } QueueCounter;
-    typedef struct calcBytesToSend {
-        bool chunk;
-        bool packet;
-        uint32 bytesToSend;
-    } BytesToBeSent;
-    typedef struct congestionControlFunctions {
-        void (SCTPAssociation::*ccInitParams)(SCTPPathVariables* path);
-        void (SCTPAssociation::*ccUpdateAfterSack)();
-        void (SCTPAssociation::*ccUpdateAfterCwndTimeout)(SCTPPathVariables* path);
-        void (SCTPAssociation::*ccUpdateAfterRtxTimeout)(SCTPPathVariables* path);
-        void (SCTPAssociation::*ccUpdateMaxBurst)(SCTPPathVariables* path);
-        void (SCTPAssociation::*ccUpdateBytesAcked)(SCTPPathVariables* path, const uint32 ackedBytes, const bool ctsnaAdvanced);
-    } CCFunctions;
-    typedef std::map<uint32, SCTPSendStream*>       SCTPSendStreamMap;
-    typedef std::map<uint32, SCTPReceiveStream*> SCTPReceiveStreamMap;
-
-    public:
-        // connection identification by apps: appgateIndex+assocId
-        int32                   appGateIndex; // Application gate index
-        int32                   assocId;        // Identifies connection within the app
-        IPvXAddress             remoteAddr; // Remote address from last message
-        IPvXAddress             localAddr;      // Local address from last message
-        uint16                  localPort;      // Remote port from last message
-        uint16                  remotePort; // Local port from last message
-        uint32                  localVTag;      // Local verification tag
-        uint32                  peerVTag;       // Remote verification tag
-        bool                    listen;
-
-        // Timers
-        cMessage*               T1_InitTimer;
-        cMessage*               T2_ShutdownTimer;
-        cMessage*               T5_ShutdownGuardTimer;
-        cMessage*               SackTimer;
-        cMessage*               StartTesting;
-
-    protected:
-        AddressVector           localAddressList;
-        AddressVector           remoteAddressList;
-        uint32                  numberOfRemoteAddresses;
-        uint32                  inboundStreams;
-        uint32                  outboundStreams;
-
-        int32                   status;
-        uint32                  initTsn;
-        uint32                  initPeerTsn;
-        uint32                  sackFrequency;
-        double                  sackPeriod;
-        CCFunctions             ccFunctions;
-        uint16                  ccModule;
-
-        cOutVector*             advRwnd;
-        cOutVector*             cumTsnAck;
-        cOutVector*             sendQueue;
-        cOutVector*             numGapBlocks;
-
-        // Variables associated with the state of this association
-        SCTPStateVariables*     state;
-        BytesToBeSent           bytes;
-        SCTP*                   sctpMain;                   // SCTP module
-        cFSM*                   fsm;                            // SCTP state machine
-        SCTPPathMap             sctpPathMap;
-        QueueCounter            qCounter;
-        SCTPQueue*              transmissionQ;
-        SCTPQueue*              retransmissionQ;
-        SCTPSendStreamMap       sendStreams;
-        SCTPReceiveStreamMap    receiveStreams;
-        SCTPAlgorithm*          sctpAlgorithm;
-
-    public:
-        /**
-        * Constructor.
-        */
-        SCTPAssociation(SCTP* mod, int32 appGateIndex, int32 assocId);
-        /**
-        * Destructor.
-        */
-        ~SCTPAssociation();
-        /**
-        * Utility: Send data from sendQueue.
-        */
-        void sendOnPath(SCTPPathVariables* pathId, const bool firstPass = true);
-        void sendOnAllPaths(SCTPPathVariables* firstPath);
-
-        /** Utility: returns name of SCTP_I_xxx constants */
-        static const char* indicationName(const int32 code);
-
-        /* @name Various getters */
-        //@{
-        inline int32 getFsmState() const { return fsm->getState(); };
-        inline SCTPStateVariables* getState() const { return state; };
-        inline SCTPQueue* getTransmissionQueue() const { return transmissionQ; };
-        inline SCTPQueue* getRetransmissionQueue() const { return retransmissionQ; };
-        inline SCTPAlgorithm* getSctpAlgorithm() const { return sctpAlgorithm; };
-        inline SCTP* getSctpMain() const { return sctpMain; };
-        inline cFSM* getFsm() const { return fsm; };
-        inline cMessage* getInitTimer() const { return T1_InitTimer; };
-        inline cMessage* getShutdownTimer() const { return T2_ShutdownTimer; };
-        inline cMessage* getSackTimer() const { return SackTimer; };
-
-        /** Utility: returns name of SCTP_S_xxx constants */
-        static const char* stateName(const int32 state);
-
-        static uint32 chunkToInt(const char* type);
-
-        /* Process self-messages (timers).
-        * Normally returns true. A return value of false means that the
-        * connection structure must be deleted by the caller (SCTPMain).
-        */
-        bool processTimer(cMessage* msg);
-        /**
-        * Process incoming SCTP segment. Normally returns true. A return value
-        * of false means that the connection structure must be deleted by the
-        * caller (SCTP).
-        */
-        bool processSCTPMessage(SCTPMessage* sctpmsg, const IPvXAddress& srcAddr, const IPvXAddress& destAddr);
-        /**
-        * Process commands from the application.
-        * Normally returns true. A return value of false means that the
-        * connection structure must be deleted by the caller (SCTP).
-        */
-        bool processAppCommand(cPacket* msg);
-        void removePath();
-        void removePath(const IPvXAddress& addr);
-        void deleteStreams();
-        void stopTimer(cMessage* timer);
-        void stopTimers();
-        inline SCTPPathVariables* getPath(const IPvXAddress& pathId) const {
-            SCTPPathMap::const_iterator iterator = sctpPathMap.find(pathId);
-            if (iterator !=sctpPathMap.end()) {
-                return iterator->second;
-            }
-            return NULL;
-        }
-        void printSctpPathMap() const;
-
-
-    protected:
-        /** @name FSM transitions: analysing events and executing state transitions */
-        //@{
-        /** Maps app command codes (msg kind of app command msgs) to SCTP_E_xxx event codes */
-        SCTPEventCode preanalyseAppCommandEvent(int32 commandCode);
-        /** Implemements the pure SCTP state machine */
-        bool performStateTransition(const SCTPEventCode& event);
-        void stateEntered(int32 state);
-        //@}
-        /** @name Processing app commands. Invoked from processAppCommand(). */
-        //@{
-        void process_ASSOCIATE(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg);
-        void process_OPEN_PASSIVE(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg);
-        void process_SEND(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg);
-        void process_CLOSE(SCTPEventCode& event);
-        void process_ABORT(SCTPEventCode& event);
-        void process_STATUS(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg);
-        void process_RECEIVE_REQUEST(SCTPEventCode& event, SCTPCommand* sctpCommand);
-        void process_PRIMARY(SCTPEventCode& event, SCTPCommand* sctpCommand);
-        //@}
-
-        /** @name Processing SCTP message arrivals. Invoked from processSCTPMessage(). */
-        //@{
-        bool process_RCV_Message(SCTPMessage* sctpseg, const IPvXAddress& src, const IPvXAddress& dest);
-        /**
-        * Process incoming SCTP packets. Invoked from process_RCV_Message
-        */
-        bool processInitArrived(SCTPInitChunk* initChunk, int32 sport, int32 dport);
-        bool processInitAckArrived(SCTPInitAckChunk* initAckChunk);
-        bool processCookieEchoArrived(SCTPCookieEchoChunk* cookieEcho, IPvXAddress addr);
-        bool processCookieAckArrived();
-        SCTPEventCode processDataArrived(SCTPDataChunk* dataChunk);
-        SCTPEventCode processSackArrived(SCTPSackChunk* sackChunk);
-        SCTPEventCode processHeartbeatAckArrived(SCTPHeartbeatAckChunk* heartbeatack, SCTPPathVariables* path);
-        //@}
-
-        /** @name Processing timeouts. Invoked from processTimer(). */
-        //@{
-        int32 process_TIMEOUT_RTX(SCTPPathVariables* path);
-        void process_TIMEOUT_HEARTBEAT(SCTPPathVariables* path);
-        void process_TIMEOUT_HEARTBEAT_INTERVAL(SCTPPathVariables* path, bool force);
-        void process_TIMEOUT_INIT_REXMIT(SCTPEventCode& event);
-        void process_TIMEOUT_PROBING();
-        void process_TIMEOUT_SHUTDOWN(SCTPEventCode& event);
-        int32 updateCounters(SCTPPathVariables* path);
-        //@}
-
-        void startTimer(cMessage* timer, const simtime_t& timeout);
-
-        /** Utility: clone a listening association. Used for forking. */
-        SCTPAssociation* cloneAssociation();
-
-        /** Utility: creates send/receive queues and sctpAlgorithm */
-        void initAssociation(SCTPOpenCommand* openCmd);
-
-        /** Methods dealing with the handling of TSNs  **/
-        bool tsnIsDuplicate(const uint32 tsn) const;
-        bool advanceCtsna();
-        bool updateGapList(const uint32 tsn);
-        void removeFromGapList(const uint32 removedTsn);
-        bool makeRoomForTsn(const uint32 tsn, const uint32 length, const bool uBit);
-
-        /** Methods for creating and sending chunks */
-        void sendInit();
-        void sendInitAck(SCTPInitChunk* initchunk);
-        void sendCookieEcho(SCTPInitAckChunk* initackchunk);
-        void sendCookieAck(const IPvXAddress& dest);
-        void sendAbort();
-        void sendHeartbeat(const SCTPPathVariables* path);
-        void sendHeartbeatAck(const SCTPHeartbeatChunk* heartbeatChunk,
-                                     const IPvXAddress&         src,
-                                     const IPvXAddress&         dest);
-        void sendSack();
-        void sendShutdown();
-        void sendShutdownAck(const IPvXAddress& dest);
-        void sendShutdownComplete();
-        SCTPSackChunk* createSack();
-        /** Retransmitting chunks */
-        void retransmitInit();
-        void retransmitCookieEcho();
-        void retransmitShutdown();
-        void retransmitShutdownAck();
-
-        /** Utility: adds control info to message and sends it to IP */
-        void sendToIP(SCTPMessage* sctpmsg, const IPvXAddress& dest, const bool qs = false);
-        inline void sendToIP(SCTPMessage* sctpmsg, const bool qs = false) {
-            sendToIP(sctpmsg, remoteAddr, qs);
-        }
-        void recordInPathVectors(SCTPMessage* pMsg, const IPvXAddress& rDest);
-        void scheduleSack();
-        /** Utility: signal to user that connection timed out */
-        void signalConnectionTimeout();
-
-        /** Utility: start a timer */
-        inline void scheduleTimeout(cMessage* msg, const simtime_t& timeout) {
-            sctpMain->scheduleAt(simulation.getSimTime() + timeout, msg);
-        }
-
-        /** Utility: cancel a timer */
-        inline cMessage* cancelEvent(cMessage* msg) {
-            return sctpMain->cancelEvent(msg);
-        }
-
-        /** Utility: sends packet to application */
-        void sendToApp(cPacket* msg);
-
-        /** Utility: sends status indication (SCTP_I_xxx) to application */
-        void sendIndicationToApp(const int32 code, const int32 value = 0);
-
-        /** Utility: sends SCTP_I_ESTABLISHED indication with SCTPConnectInfo to application */
-        void sendEstabIndicationToApp();
-        void pushUlp();
-        void sendDataArrivedNotification(uint16 sid);
-        void putInDeliveryQ(uint16 sid);
-        /** Utility: prints local/remote addr/port and app gate index/assocId */
-        void printConnBrief();
-        /** Utility: prints important header fields */
-        static void printSegmentBrief(SCTPMessage* sctpmsg);
-
-
-        /** Utility: returns name of SCTP_E_xxx constants */
-        static const char* eventName(const int32 event);
-
-        void addPath(const IPvXAddress& addr);
-        SCTPPathVariables* getNextPath(const SCTPPathVariables* oldPath) const;
-        inline const IPvXAddress& getNextAddress(const SCTPPathVariables* oldPath) const {
-            const SCTPPathVariables* nextPath = getNextPath(oldPath);
-            if(nextPath != NULL) {
-                return(nextPath->remoteAddress);
-            }
-            return(SCTPDataVariables::zeroAddress);
-        }
-        SCTPPathVariables* getNextDestination(SCTPDataVariables* chunk) const;
-
-        void bytesAllowedToSend(SCTPPathVariables* path, const bool firstPass);
-
-        void pathStatusIndication(const SCTPPathVariables* path, const bool status);
-
-        bool allPathsInactive() const;
-
-        /**
-        * Manipulating chunks
-        */
-        SCTPDataChunk* transformDataChunk(SCTPDataVariables* chunk);
-        SCTPDataVariables* makeVarFromMsg(SCTPDataChunk* datachunk);
-
-        /**
-        *Dealing with streams
-        */
-
-        int32 streamScheduler(bool peek);
-        void initStreams(uint32 inStreams, uint32 outStreams);
-        int32 numUsableStreams();
-        typedef struct streamSchedulingFunctions {
-            void (SCTPAssociation::*ssInitStreams)(uint32 inStreams, uint32 outStreams);
-            int32 (SCTPAssociation::*ssGetNextSid)(bool peek);
-            int32 (SCTPAssociation::*ssUsableStreams)();
-        } SSFunctions;
-        SSFunctions ssFunctions;
-        uint16 ssModule;
-
-        /**
-        *    Queue Management
-        */
-        void process_QUEUE_MSGS_LIMIT(const SCTPCommand* sctpCommand);
-        void process_QUEUE_BYTES_LIMIT(const SCTPCommand* sctpCommand);
-        int32 getOutstandingBytes() const;
-        uint32 dequeueAckedChunks(const uint32          tsna,
-                                          SCTPPathVariables* path,
-                                          simtime_t&            rttEstimation);
-        SCTPDataMsg* peekOutboundDataMsg();
-        SCTPDataVariables* peekAbandonedChunk(const SCTPPathVariables* path);
-        SCTPDataVariables* getOutboundDataChunk(const SCTPPathVariables* path,
-                                                             const int32                  availableSpace,
-                                                             const int32                  availableCwnd);
-        SCTPDataMsg* dequeueOutboundDataMsg(const int32 availableSpace,
-                                                        const int32 availableCwnd);
-        bool nextChunkFitsIntoPacket(int32 bytes);
-        void putInTransmissionQ(uint32 tsn, SCTPDataVariables* chunk);
-        /**
-        * Flow control
-        */
-        void pmStartPathManagement();
-        void pmDataIsSentOn(SCTPPathVariables* path);
-        void pmClearPathCounter(SCTPPathVariables* path);
-        void pmRttMeasurement(SCTPPathVariables* path,
-                                     const simtime_t&     rttEstimation);
-        /**
-        * Compare TSNs
-        */
-        inline static int32 tsnLt (const uint32 tsn1, const uint32 tsn2) { return ((int32)(tsn1-tsn2)<0); }
-        inline static int32 tsnLe (const uint32 tsn1, const uint32 tsn2) { return ((int32)(tsn1-tsn2)<=0); }
-        inline static int32 tsnGe (const uint32 tsn1, const uint32 tsn2) { return ((int32)(tsn1-tsn2)>=0); }
-        inline static int32 tsnGt (const uint32 tsn1, const uint32 tsn2) { return ((int32)(tsn1-tsn2)>0); }
-        inline static int32 tsnBetween (const uint32 tsn1, const uint32 midtsn, const uint32 tsn2) { return ((tsn2-tsn1)>=(midtsn-tsn1)); }
-
-        inline static int16 ssnGt (const uint16 ssn1, const uint16 ssn2) { return ((int16)(ssn1-ssn2)>0); }
-
-        void disposeOf(SCTPMessage* sctpmsg);
-        void tsnWasReneged(SCTPDataVariables*         chunk,
-                                 const int                    type);
-        void printOutstandingTsns();
-
-        /** SCTPCCFunctions **/
-        void initCCParameters(SCTPPathVariables* path);
-        void updateFastRecoveryStatus(const uint32 lastTsnAck);
-        void cwndUpdateAfterSack();
-        void cwndUpdateAfterCwndTimeout(SCTPPathVariables* path);
-        void cwndUpdateAfterRtxTimeout(SCTPPathVariables* path);
-        void cwndUpdateMaxBurst(SCTPPathVariables* path);
-        void cwndUpdateBytesAcked(SCTPPathVariables* path,
-                                          const uint32          ackedBytes,
-                                          const bool            ctsnaAdvanced);
-
-    private:
-        SCTPDataVariables* makeDataVarFromDataMsg(SCTPDataMsg*       datMsg,
-                                                                SCTPPathVariables* path);
-        SCTPPathVariables* choosePathForRetransmission();
-        void timeForSack(bool& sackOnly, bool& sackWithData);
-        void recordCwndUpdate(SCTPPathVariables* path);
-        void handleChunkReportedAsAcked(uint32&             highestNewAck,
-                                                  simtime_t&            rttEstimation,
-                                                  SCTPDataVariables* myChunk,
-                                                  SCTPPathVariables* sackPath);
-        void handleChunkReportedAsMissing(const SCTPSackChunk*    sackChunk,
-                                                     const uint32                 highestNewAck,
-                                                     SCTPDataVariables*       myChunk,
-                                                     const SCTPPathVariables* sackPath);
-        void moveChunkToOtherPath(SCTPDataVariables* chunk,
-                                          SCTPPathVariables* newPath);
-        void decreaseOutstandingBytes(SCTPDataVariables* chunk);
-        void increaseOutstandingBytes(SCTPDataVariables* chunk,
+   friend class SCTP;
+   friend class SCTPPathVariables;
+
+   // map for storing the path parameters
+   typedef std::map<IPvXAddress,SCTPPathVariables*> SCTPPathMap;
+   // map for storing the queued bytes per path
+   typedef std::map<IPvXAddress, uint32> CounterMap;
+   typedef struct counter {
+      uint64     roomSumSendStreams;
+      uint64     bookedSumSendStreams;
+      uint64     roomSumRcvStreams;
+      CounterMap roomTransQ;
+      CounterMap bookedTransQ;
+      CounterMap roomRetransQ;
+   } QueueCounter;
+   typedef struct calcBytesToSend {
+      bool chunk;
+      bool packet;
+      uint32 bytesToSend;
+   } BytesToBeSent;
+   typedef struct congestionControlFunctions {
+      void (SCTPAssociation::*ccInitParams)(SCTPPathVariables* path);
+      void (SCTPAssociation::*ccUpdateBeforeSack)();
+      void (SCTPAssociation::*ccUpdateAfterSack)();
+      void (SCTPAssociation::*ccUpdateAfterCwndTimeout)(SCTPPathVariables* path);
+      void (SCTPAssociation::*ccUpdateAfterRtxTimeout)(SCTPPathVariables* path);
+      void (SCTPAssociation::*ccUpdateMaxBurst)(SCTPPathVariables* path);
+      void (SCTPAssociation::*ccUpdateBytesAcked)(SCTPPathVariables* path, const uint32 ackedBytes, const bool ctsnaAdvanced);
+   } CCFunctions;
+   typedef std::map<uint32, SCTPSendStream*>    SCTPSendStreamMap;
+   typedef std::map<uint32, SCTPReceiveStream*> SCTPReceiveStreamMap;
+
+   public:
+      // connection identification by apps: appgateIndex+assocId
+      int32                 appGateIndex; // Application gate index
+      int32                 assocId;      // Identifies connection within the app
+      IPvXAddress           remoteAddr;   // Remote address from last message
+      IPvXAddress           localAddr;    // Local address from last message
+      uint16                localPort;    // Remote port from last message
+      uint16                remotePort;   // Local port from last message
+      uint32                localVTag;    // Local verification tag
+      uint32                peerVTag;     // Remote verification tag
+      bool                  listen;
+
+      // Timers
+      cMessage*             T1_InitTimer;
+      cMessage*             T2_ShutdownTimer;
+      cMessage*             T5_ShutdownGuardTimer;
+      cMessage*             SackTimer;
+      cMessage*             StartTesting;
+      cMessage*             StartAddIP;
+      cOutVector*           advMsgRwnd;
+      cOutVector*           EndToEndDelay;
+      bool                  fairTimer;
+      std::map<uint16,cOutVector*> streamThroughputVectors;
+      cOutVector*           assocThroughputVector;
+      cMessage*             FairStartTimer;
+      cMessage*             FairStopTimer;
+
+
+   protected:
+      AddressVector         localAddressList;
+      AddressVector         remoteAddressList;
+      uint32                numberOfRemoteAddresses;
+      uint32                inboundStreams;
+      uint32                outboundStreams;
+
+      int32                 status;
+      uint32                initTsn;
+      uint32                initPeerTsn;
+      uint32                sackFrequency;
+      double                sackPeriod;
+      CCFunctions           ccFunctions;
+      uint16                ccModule;
+
+      cOutVector*           advRwnd;
+      cOutVector*           cumTsnAck;
+      cOutVector*           sendQueue;
+      cOutVector*           numGapBlocks;
+      // ------ Transmission Statistics -------------------------------------
+      TimeStatsCollector*   statisticsOutstandingBytes;
+      TimeStatsCollector*   statisticsQueuedReceivedBytes;
+      TimeStatsCollector*   statisticsQueuedSentBytes;
+      TimeStatsCollector*   statisticsTotalSSthresh;
+      TimeStatsCollector*   statisticsTotalCwnd;
+      TimeStatsCollector*   statisticsTotalBandwidth;
+      // ------ Received SACK Statistics ------------------------------------
+      TimeStatsCollector*   statisticsRevokableGapBlocksInLastSACK;    // T.D. 18.10.2010: Revokable GapAck blocks in last received SACK
+      TimeStatsCollector*   statisticsNonRevokableGapBlocksInLastSACK; // T.D. 18.10.2010: Non-Revokable GapAck blocks in last received SACK
+      TimeStatsCollector*   statisticsArwndInLastSACK;
+      TimeStatsCollector*   statisticsPeerRwnd;
+      // ------ Sent SACK Statistics ----------------------------------------
+      TimeStatsCollector*   statisticsNumTotalGapBlocksStored;         // T.D. 18.10.2010: Number of GapAck blocks stored (NOTE: R + NR!)
+      TimeStatsCollector*   statisticsNumRevokableGapBlocksStored;     // T.D. 18.10.2010: Number of Revokable GapAck blocks stored
+      TimeStatsCollector*   statisticsNumNonRevokableGapBlocksStored;  // T.D. 18.10.2010: Number of Non-Revokable GapAck blocks stored
+      TimeStatsCollector*   statisticsNumDuplicatesStored;             // T.D. 18.10.2010: Number of duplicate TSNs stored
+      TimeStatsCollector*   statisticsNumRevokableGapBlocksSent;       // T.D. 18.10.2010: Number of Revokable GapAck blocks sent in last SACK
+      TimeStatsCollector*   statisticsNumNonRevokableGapBlocksSent;    // T.D. 18.10.2010: Number of Non-Revokable GapAck blocks sent in last SACK
+      TimeStatsCollector*   statisticsNumDuplicatesSent;               // T.D. 18.10.2010: Number of duplicate TSNs sent in last SACK
+      TimeStatsCollector*   statisticsSACKLengthSent;                  // T.D. 18.10.2010: Length of last sent SACK
+
+      // Variables associated with the state of this association
+      SCTPStateVariables*   state;
+      BytesToBeSent         bytes;
+      SCTP*                 sctpMain;              // SCTP module
+      cFSM*                 fsm;                   // SCTP state machine
+      SCTPPathMap           sctpPathMap;
+      QueueCounter          qCounter;
+      ChunkMap*             chunkMap;
+      SCTPQueue*            transmissionQ;
+      SCTPQueue*            retransmissionQ;
+      SCTPSendStreamMap     sendStreams;
+      SCTPReceiveStreamMap  receiveStreams;
+      SCTPAlgorithm*        sctpAlgorithm;
+
+   public:
+      /**
+      * Constructor.
+      */
+      SCTPAssociation(SCTP* mod, int32 appGateIndex, int32 assocId);
+      /**
+      * Destructor.
+      */
+      ~SCTPAssociation();
+      /**
+      * Utility: Send data from sendQueue.
+      */
+      void sendOnPath(SCTPPathVariables* pathId, const bool firstPass = true);
+      void sendOnAllPaths(SCTPPathVariables* firstPath);
+
+      /** Utility: returns name of SCTP_I_xxx constants */
+      static const char* indicationName(const int32 code);
+
+      /* @name Various getters */
+      //@{
+      inline int32 getFsmState() const { return fsm->getState(); };
+      inline SCTPStateVariables* getState() const { return state; };
+      inline SCTPQueue* getTransmissionQueue() const { return transmissionQ; };
+      inline SCTPQueue* getRetransmissionQueue() const { return retransmissionQ; };
+      inline SCTPAlgorithm* getSctpAlgorithm() const { return sctpAlgorithm; };
+      inline SCTP* getSctpMain() const { return sctpMain; };
+      inline cFSM* getFsm() const { return fsm; };
+      inline cMessage* getInitTimer() const { return T1_InitTimer; };
+      inline cMessage* getShutdownTimer() const { return T2_ShutdownTimer; };
+      inline cMessage* getSackTimer() const { return SackTimer; };
+
+      /** Utility: returns name of SCTP_S_xxx constants */
+      static const char* stateName(const int32 state);
+
+      /* Process self-messages (timers).
+      * Normally returns true. A return value of false means that the
+      * connection structure must be deleted by the caller (SCTPMain).
+      */
+      bool processTimer(cMessage* msg);
+      /**
+      * Process incoming SCTP segment. Normally returns true. A return value
+      * of false means that the connection structure must be deleted by the
+      * caller (SCTP).
+      */
+      bool processSCTPMessage(SCTPMessage* sctpmsg, const IPvXAddress& srcAddr, const IPvXAddress& destAddr);
+      /**
+      * Process commands from the application.
+      * Normally returns true. A return value of false means that the
+      * connection structure must be deleted by the caller (SCTP).
+      */
+      bool processAppCommand(cPacket* msg);
+      void removePath();
+      void removePath(const IPvXAddress& addr);
+      void deleteStreams();
+      void stopTimer(cMessage* timer);
+      void stopTimers();
+      inline SCTPPathVariables* getPath(const IPvXAddress& pathId) const {
+         SCTPPathMap::const_iterator iterator = sctpPathMap.find(pathId);
+         if (iterator !=sctpPathMap.end()) {
+            return iterator->second;
+         }
+         return NULL;
+      }
+      void printSctpPathMap() const;
+
+
+   protected:
+      /** @name FSM transitions: analysing events and executing state transitions */
+      //@{
+      /** Maps app command codes (msg kind of app command msgs) to SCTP_E_xxx event codes */
+      SCTPEventCode preanalyseAppCommandEvent(int32 commandCode);
+      /** Implemements the pure SCTP state machine */
+      bool performStateTransition(const SCTPEventCode& event);
+      void stateEntered(int32 state);
+      //@}
+      /** @name Processing app commands. Invoked from processAppCommand(). */
+      //@{
+      void process_ASSOCIATE(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg);
+      void process_OPEN_PASSIVE(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg);
+      void process_SEND(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg);
+      void process_CLOSE(SCTPEventCode& event);
+      void process_ABORT(SCTPEventCode& event);
+      void process_STATUS(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg);
+      void process_RECEIVE_REQUEST(SCTPEventCode& event, SCTPCommand* sctpCommand);
+      void process_PRIMARY(SCTPEventCode& event, SCTPCommand* sctpCommand);
+      void process_STREAM_RESET(SCTPCommand* sctpCommand);
+      //@}
+
+      /** @name Processing SCTP message arrivals. Invoked from processSCTPMessage(). */
+      //@{
+      bool process_RCV_Message(SCTPMessage* sctpseg, const IPvXAddress& src, const IPvXAddress& dest);
+      /**
+      * Process incoming SCTP packets. Invoked from process_RCV_Message
+      */
+      bool processInitArrived(SCTPInitChunk* initChunk, int32 sport, int32 dport);
+      bool processInitAckArrived(SCTPInitAckChunk* initAckChunk);
+      bool processCookieEchoArrived(SCTPCookieEchoChunk* cookieEcho, IPvXAddress addr);
+      bool processCookieAckArrived();
+      SCTPEventCode processDataArrived(SCTPDataChunk* dataChunk);
+      SCTPEventCode processSackArrived(SCTPSackChunk* sackChunk);
+      SCTPEventCode processHeartbeatAckArrived(SCTPHeartbeatAckChunk* heartbeatack, SCTPPathVariables* path);
+      SCTPEventCode processForwardTsnArrived(SCTPForwardTsnChunk* forChunk);
+      bool processPacketDropArrived(SCTPPacketDropChunk* pktdrop);
+      void processErrorArrived(SCTPErrorChunk* error);
+      //@}
+
+      /** @name Processing timeouts. Invoked from processTimer(). */
+      //@{
+      void process_TIMEOUT_RTX(SCTPPathVariables* path);
+      void process_TIMEOUT_BLOCKING(SCTPPathVariables* path);
+      void process_TIMEOUT_HEARTBEAT(SCTPPathVariables* path);
+      void process_TIMEOUT_HEARTBEAT_INTERVAL(SCTPPathVariables* path, bool force);
+      void process_TIMEOUT_INIT_REXMIT(SCTPEventCode& event);
+      void process_TIMEOUT_RESET(SCTPPathVariables* path);
+      void process_TIMEOUT_ASCONF(SCTPPathVariables* path);
+      void process_TIMEOUT_PROBING();
+      void process_TIMEOUT_SHUTDOWN(SCTPEventCode& event);
+      int32 updateCounters(SCTPPathVariables* path);
+      //@}
+
+      void startTimer(cMessage* timer, const simtime_t& timeout);
+
+      /** Utility: clone a listening association. Used for forking. */
+      SCTPAssociation* cloneAssociation();
+
+      /** Utility: creates send/receive queues and sctpAlgorithm */
+      void initAssociation(SCTPOpenCommand* openCmd);
+
+      /** Methods dealing with the handling of TSNs  **/
+      bool tsnIsDuplicate(const uint32 tsn) const;
+      bool makeRoomForTsn(const uint32 tsn, const uint32 length, const bool uBit);
+
+      /** Methods for creating and sending chunks */
+      void sendInit();
+      void sendInitAck(SCTPInitChunk* initchunk);
+      void sendCookieEcho(SCTPInitAckChunk* initackchunk);
+      void sendCookieAck(const IPvXAddress& dest);
+      void sendAbort();
+      void sendHeartbeat(const SCTPPathVariables* path);
+      void sendHeartbeatAck(const SCTPHeartbeatChunk* heartbeatChunk,
+                            const IPvXAddress&        src,
+                            const IPvXAddress&        dest);
+      void sendSack();
+      void sendShutdown();
+      void sendShutdownAck(const IPvXAddress& dest);
+      void sendShutdownComplete();
+      SCTPSackChunk* createSack();
+      void sendStreamResetRequest(uint16 type);
+      void sendStreamResetResponse(uint32 srrsn);
+      void sendStreamResetResponse(SCTPSSNTSNResetRequestParameter* requestParam,
+                                   bool                             options);
+      void sendOutgoingResetRequest(SCTPIncomingSSNResetRequestParameter* requestParam);
+      void sendPacketDrop(const bool flag);
+      void sendHMacError(const uint16 id);
+
+      SCTPForwardTsnChunk* createForwardTsnChunk(const IPvXAddress& pid);
+
+      /** Retransmitting chunks */
+      void retransmitInit();
+      void retransmitCookieEcho();
+      void retransmitReset();
+      void retransmitShutdown();
+      void retransmitShutdownAck();
+
+      /** Utility: adds control info to message and sends it to IP */
+      void sendToIP(SCTPMessage* sctpmsg, const IPvXAddress& dest);
+      inline void sendToIP(SCTPMessage* sctpmsg) {
+         sendToIP(sctpmsg, remoteAddr);
+      }
+      void scheduleSack();
+      /** Utility: signal to user that connection timed out */
+      void signalConnectionTimeout();
+
+      /** Utility: start a timer */
+      inline void scheduleTimeout(cMessage* msg, const simtime_t& timeout) {
+         sctpMain->scheduleAt(simulation.getSimTime() + timeout, msg);
+      }
+
+      /** Utility: cancel a timer */
+      inline cMessage* cancelEvent(cMessage* msg) {
+         return sctpMain->cancelEvent(msg);
+      }
+
+      /** Utility: sends packet to application */
+      void sendToApp(cPacket* msg);
+
+      /** Utility: sends status indication (SCTP_I_xxx) to application */
+      void sendIndicationToApp(const int32 code, const int32 value = 0);
+
+      /** Utility: sends SCTP_I_ESTABLISHED indication with SCTPConnectInfo to application */
+      void sendEstabIndicationToApp();
+      void pushUlp();
+      void sendDataArrivedNotification(const uint16 sid);
+      void putInDeliveryQ(const uint16 sid);
+      bool msgMustBeAbandoned(SCTPDataMsg* msg, int32 stream, bool ordered); //PR-SCTP
+      void advancePeerTsn();
+
+      /** Utility: prints local/remote addr/port and app gate index/assocId */
+      void printAssocBrief();
+      /** Utility: prints important header fields */
+      static void printSegmentBrief(SCTPMessage* sctpmsg);
+
+
+      /** Utility: returns name of SCTP_E_xxx constants */
+      static const char* eventName(const int32 event);
+
+      void addPath(const IPvXAddress& addr);
+      SCTPPathVariables* getNextPath(const SCTPPathVariables* oldPath) const;
+      inline const IPvXAddress& getNextAddress(const SCTPPathVariables* oldPath) const {
+         const SCTPPathVariables* nextPath = getNextPath(oldPath);
+         if(nextPath != NULL) {
+            return(nextPath->remoteAddress);
+         }
+         return(SCTPDataVariables::zeroAddress);
+      }
+      SCTPPathVariables* getNextDestination(const SCTPDataVariables* chunk) const;
+
+      void bytesAllowedToSend(SCTPPathVariables* path, const bool firstPass);
+
+      void pathStatusIndication(const SCTPPathVariables* path, const bool status);
+
+      bool allPathsInactive() const;
+
+      /**
+      * Manipulating chunks
+      */
+      SCTPDataChunk* transformDataChunk(SCTPDataVariables* chunk);
+      SCTPDataVariables* makeVarFromMsg(SCTPDataChunk* datachunk);
+
+      /**
+      * Dealing with streams
+      */
+
+      int32 streamScheduler(SCTPPathVariables* path, bool peek);
+      void initStreams(uint32 inStreams, uint32 outStreams);
+      int32 numUsableStreams();
+      int32 streamSchedulerRoundRobinPacket(SCTPPathVariables* path, bool peek);
+      int32 streamSchedulerRandom(SCTPPathVariables* path, bool peek);
+      int32 streamSchedulerRandomPacket(SCTPPathVariables* path, bool peek);
+      int32 streamSchedulerFairBandwidth(SCTPPathVariables* path, bool peek);
+      int32 streamSchedulerFairBandwidthPacket(SCTPPathVariables* path, bool peek);
+      int32 streamSchedulerPriority(SCTPPathVariables* path, bool peek);
+      int32 streamSchedulerFCFS(SCTPPathVariables* path, bool peek);
+      int32 pathStreamSchedulerManual(SCTPPathVariables* path, bool peek);
+      int32 pathStreamSchedulerMapToPath(SCTPPathVariables* path, bool peek);
+      typedef struct streamSchedulingFunctions {
+         void (SCTPAssociation::*ssInitStreams)(uint32 inStreams, uint32 outStreams);
+         int32 (SCTPAssociation::*ssGetNextSid)(SCTPPathVariables* path, bool peek);
+         int32 (SCTPAssociation::*ssUsableStreams)();
+      } SSFunctions;
+      SSFunctions ssFunctions;
+      uint16 ssModule;
+
+      /**
+      * Queue Management
+      */
+      void process_QUEUE_MSGS_LIMIT(const SCTPCommand* sctpCommand);
+      void process_QUEUE_BYTES_LIMIT(const SCTPCommand* sctpCommand);
+      int32 getOutstandingBytes() const;
+      void dequeueAckedChunks(const uint32       tsna,
+                              SCTPPathVariables* path,
+                              simtime_t&         rttEstimation);
+      SCTPDataMsg* peekOutboundDataMsg();
+      SCTPDataVariables* peekAbandonedChunk(const SCTPPathVariables* path);
+      SCTPDataVariables* getOutboundDataChunk(const SCTPPathVariables* path,
+                                              const int32              availableSpace,
+                                              const int32              availableCwnd);
+      SCTPDataMsg* dequeueOutboundDataMsg(SCTPPathVariables* path,
+                                          const int32 availableSpace,
+                                          const int32 availableCwnd);
+      bool nextChunkFitsIntoPacket(SCTPPathVariables* path, int32 bytes);
+      void putInTransmissionQ(uint32 tsn, SCTPDataVariables* chunk);
+
+      uint32 getAllTransQ();
+
+      /**
+      * Flow control
+      */
+      void pmStartPathManagement();
+      void pmDataIsSentOn(SCTPPathVariables* path);
+      void pmClearPathCounter(SCTPPathVariables* path);
+      void pmRttMeasurement(SCTPPathVariables* path,
+                            const simtime_t&   rttEstimation);
+
+      void disposeOf(SCTPMessage* sctpmsg);
+
+      /** Methods for Stream Reset **/
+      void resetSsns();
+      void resetExpectedSsns();
+      SCTPParameter* makeOutgoingStreamResetParameter(uint32 srsn);
+      SCTPParameter* makeIncomingStreamResetParameter(uint32 srsn);
+      SCTPParameter* makeSSNTSNResetParameter(uint32 srsn);
+      void sendOutgoingRequestAndResponse(uint32 inRequestSn, uint32 outRequestSn);
+      SCTPEventCode processInAndOutResetRequestArrived(SCTPIncomingSSNResetRequestParameter* inRequestParam, SCTPOutgoingSSNResetRequestParameter* outRequestParam);
+      SCTPEventCode processOutAndResponseArrived(SCTPOutgoingSSNResetRequestParameter* outRequestParam, SCTPStreamResetResponseParameter* responseParam);
+      SCTPEventCode processStreamResetArrived(SCTPStreamResetChunk*strResChunk);
+      void processOutgoingResetRequestArrived(SCTPOutgoingSSNResetRequestParameter* requestParam);
+      void processIncomingResetRequestArrived(SCTPIncomingSSNResetRequestParameter* requestParam);
+      void processSSNTSNResetRequestArrived(SCTPSSNTSNResetRequestParameter* requestParam);
+      void processResetResponseArrived(SCTPStreamResetResponseParameter* responseParam);
+
+      /** Methods for Add-IP and AUTH **/
+      void sendAsconf(const char* type, const bool remote = false);
+      void sendAsconfAck(const uint32 serialNumber);
+      SCTPEventCode processAsconfArrived(SCTPAsconfChunk* asconfChunk);
+      SCTPEventCode processAsconfAckArrived(SCTPAsconfAckChunk* asconfAckChunk);
+      void retransmitAsconf();
+      bool typeInChunkList(const uint16 type);
+      SCTPAsconfAckChunk*      createAsconfAckChunk(const uint32 serialNumber);
+      SCTPAuthenticationChunk* createAuthChunk();
+      SCTPSuccessIndication*   createSuccessIndication(uint32 correlationId);
+      void calculateAssocSharedKey();
+      bool compareRandom();
+
+      void makeRoutingEntry(const char* route);
+      void calculateRcvBuffer();
+      void listOrderedQ();
+      void tsnWasReneged(SCTPDataVariables*       chunk,
+                         const SCTPPathVariables* sackPath,
+                         const int                type);
+      void printOutstandingTsns();
+
+      /** SCTPCCFunctions  **/
+      void initCCParameters(SCTPPathVariables* path);
+      void updateFastRecoveryStatus(const uint32 lastTsnAck);
+      void cwndUpdateBeforeSack();
+      void cwndUpdateAfterSack();
+      void cwndUpdateAfterCwndTimeout(SCTPPathVariables* path);
+      void cwndUpdateAfterRtxTimeout(SCTPPathVariables* path);
+      void cwndUpdateMaxBurst(SCTPPathVariables* path);
+      void cwndUpdateBytesAcked(SCTPPathVariables* path,
+                                const uint32       ackedBytes,
+                                const bool         ctsnaAdvanced);
+      int32 rpPathBlockingControl(SCTPPathVariables* path, const double reduction);
+
+   private:
+      SCTPDataVariables* makeDataVarFromDataMsg(SCTPDataMsg*       datMsg,
                                                 SCTPPathVariables* path);
-        int32 calculateBytesToSendOnPath(const SCTPPathVariables* pathVar);
-        void storePacket(SCTPPathVariables* pathVar,
-                              SCTPMessage*          sctpMsg,
-                              const uint16          chunksAdded,
-                              const uint16          dataChunksAdded,
-                              const uint32          packetBytes,
-                              const bool            authAdded);
-        void loadPacket(SCTPPathVariables* pathVar,
-                             SCTPMessage**        sctpMsg,
-                             uint16*                  chunksAdded,
-                             uint16*                  dataChunksAdded,
-                             uint32*                  packetBytes,
-                             bool*                authAdded);
-        inline void ackChunk(SCTPDataVariables* chunk) {
-            chunk->hasBeenAcked = true;
-        }
-        inline void unackChunk(SCTPDataVariables* chunk) {
-            chunk->hasBeenAcked = false;
-        }
-        inline bool chunkHasBeenAcked(const SCTPDataVariables* chunk) const {
-            return(chunk->hasBeenAcked);
-        }
-        inline bool chunkHasBeenAcked(const uint32 tsn) const {
-            const SCTPDataVariables* chunk = retransmissionQ->getChunk(tsn);
-            if(chunk) {
-                return(chunkHasBeenAcked(chunk));
-            }
-            return(false);
-        }
+      SCTPPathVariables* choosePathForRetransmission();
+      void timeForSack(bool& sackOnly, bool& sackWithData);
+      void sendSACKviaSelectedPath(SCTPMessage* sctpMsg);
+      void checkOutstandingBytes();
+      uint32 getInitialCwnd(const SCTPPathVariables* path) const;
+      void recordCwndUpdate(SCTPPathVariables* path);
+      void generateSendQueueAbatedIndication(const uint64 bytes);
+      void renegablyAckChunk(SCTPDataVariables* chunk,
+                             SCTPPathVariables* sackPath);
+      void nonRenegablyAckChunk(SCTPDataVariables* chunk,
+                                SCTPPathVariables* sackPath,
+                                simtime_t&         rttEstimation,
+                                SCTP::AssocStat*   assocStat);
+      void handleChunkReportedAsAcked(uint32&            highestNewAck,
+                                      simtime_t&         rttEstimation,
+                                      SCTPDataVariables* myChunk,
+                                      SCTPPathVariables* sackPath,
+                                      const bool         sackIsNonRevokable);
+      void handleChunkReportedAsMissing(const SCTPSackChunk*     sackChunk,
+                                        const uint32             highestNewAck,
+                                        SCTPDataVariables*       myChunk,
+                                        const SCTPPathVariables* sackPath);
+      void moveChunkToOtherPath(SCTPDataVariables* chunk,
+                                SCTPPathVariables* newPath);
+      void decreaseOutstandingBytes(SCTPDataVariables* chunk);
+      void increaseOutstandingBytes(SCTPDataVariables* chunk,
+                                    SCTPPathVariables* path);
+      int32 calculateBytesToSendOnPath(const SCTPPathVariables* pathVar);
+      void storePacket(SCTPPathVariables* pathVar,
+                       SCTPMessage*       sctpMsg,
+                       const uint16       chunksAdded,
+                       const uint16       dataChunksAdded,
+                       const bool         authAdded);
+      void loadPacket(SCTPPathVariables* pathVar,
+                      SCTPMessage**      sctpMsg,
+                      uint16*            chunksAdded,
+                      uint16*            dataChunksAdded,
+                      bool*              authAdded);
+
+      inline void ackChunk(SCTPDataVariables* chunk, SCTPPathVariables* sackPath) {
+         chunkMap->ack(chunk->tsn);
+         chunk->hasBeenAcked = true;
+         chunk->ackedOnPath  = sackPath;
+      }
+      inline void unackChunk(SCTPDataVariables* chunk) {
+         chunkMap->unack(chunk->tsn);
+         chunk->hasBeenAcked = false;
+      }
+      inline bool chunkHasBeenAcked(const SCTPDataVariables* chunk) const {
+         return(chunk->hasBeenAcked);
+      }
+      inline bool chunkHasBeenAcked(const uint32 tsn) const {
+         return(chunkMap->isAcked(tsn));
+      }
+
+      void recordTransmission(SCTPMessage* sctpMsg, SCTPPathVariables* path);
+      void recordAcknowledgement(SCTPDataVariables* chunk, SCTPPathVariables* path);
+      void recordDequeuing(SCTPDataVariables* chunk);
+
+
+      void dumpPaths(std::ostream& os = sctpEV3) const;
+      void dumpQueue(std::ostream& os = sctpEV3);
+
+      inline bool addAuthChunkIfNecessary(SCTPMessage* sctpMsg,
+                                          const uint16 chunkType,
+                                          const bool   authAdded) {
+         // ------ Create AUTH chunk, if necessary -----------------------------
+         if ((state->auth) &&
+            (state->peerAuth) &&
+            (typeInChunkList(chunkType)) &&
+            (authAdded == false)) {
+            SCTPAuthenticationChunk* authChunk = createAuthChunk();
+            sctpMsg->addChunk(authChunk);
+            return(true);
+         }
+         return(false);
+      }
 };
 
 #endif
diff --git a/src/transport/sctp/SCTPAssociationAddIP.cc b/src/transport/sctp/SCTPAssociationAddIP.cc
new file mode 100644
index 0000000..ec0f758
--- /dev/null
+++ b/src/transport/sctp/SCTPAssociationAddIP.cc
@@ -0,0 +1,328 @@
+//
+// Copyright (C) 2008-2010 Irene Ruengeler
+// Copyright (C) 2012 Thomas Dreibholz
+//
+// 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
+// of the License, 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 this program; if not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "IPAddressResolver.h"
+#include "SCTPAssociation.h"
+
+void SCTPAssociation::sendAsconf(const char* type, const bool remote)
+{
+   SCTPAuthenticationChunk* authChunk;
+   char*                    token;
+   bool                     nat         = false;
+   IPvXAddress              targetAddr  = remoteAddr;
+   uint16                   chunkLength = 0;
+
+   if (state->asconfOutstanding == false) {
+      sctpEV3 << "sendAsconf\n";
+      SCTPMessage *sctpAsconf = new SCTPMessage("ASCONF-MSG");
+      sctpAsconf->setByteLength(SCTP_COMMON_HEADER);
+      sctpAsconf->setSrcPort(localPort);
+      sctpAsconf->setDestPort(remotePort);
+      SCTPAsconfChunk *asconfChunk=new SCTPAsconfChunk("ASCONF-CHUNK");
+      asconfChunk->setChunkType(ASCONF);
+      asconfChunk->setSerialNumber(state->asconfSn);
+      chunkLength = SCTP_ADD_IP_CHUNK_LENGTH;
+      sctpEV3 << "localAddr="<<localAddr<<", remoteAddr="<<remoteAddr<<"\n";
+      if (getLevel(localAddr)==3 && getLevel(remoteAddr)==4 && (bool)sctpMain->par("natFriendly")) {
+         asconfChunk->setAddressParam(IPvXAddress("0.0.0.0"));
+         asconfChunk->setPeerVTag(peerVTag);
+         nat = true;
+      }
+      else {
+         asconfChunk->setAddressParam(localAddr);
+      }
+      if (localAddr.isIPv6()) {
+         chunkLength+=20;
+      }
+      else {
+         chunkLength+=8;
+      }
+      asconfChunk->setBitLength(chunkLength);
+      token = strtok((char*)type,",");
+      while (token != NULL)
+      {
+         switch (atoi(token))
+         {
+            case ADD_IP_ADDRESS:
+            {
+               SCTPAddIPParameter* ipParam;
+               ipParam = new SCTPAddIPParameter("AddIP");
+               chunkLength += SCTP_ADD_IP_PARAMETER_LENGTH;
+               ipParam->setParameterType(ADD_IP_ADDRESS);
+               ipParam->setRequestCorrelationId(++state->corrIdNum);
+               if (nat) {
+                  ipParam->setAddressParam(IPvXAddress("0.0.0.0"));
+                  sctpMain->addLocalAddressToAllRemoteAddresses(this, IPAddressResolver().resolve(sctpMain->par("addAddress"), 1), (std::vector<IPvXAddress>) remoteAddressList);
+                  state->localAddresses.push_back(IPAddressResolver().resolve(sctpMain->par("addAddress"), 1));
+                  if (remote)
+                     targetAddr = remoteAddr;
+                  else
+                     targetAddr=getNextAddress(getPath(remoteAddr));
+               }
+               else {
+                  ipParam->setAddressParam(IPAddressResolver().resolve(sctpMain->par("addAddress"), 1));
+               }
+               if (ipParam->getAddressParam().isIPv6()) {
+                  chunkLength += 20;
+                  ipParam->setBitLength((SCTP_ADD_IP_PARAMETER_LENGTH+20)*8);
+               }
+               else {
+                  chunkLength += 8;
+                  ipParam->setBitLength((SCTP_ADD_IP_PARAMETER_LENGTH+8)*8);
+               }
+               asconfChunk->addAsconfParam(ipParam);
+               break;
+            }
+            case DELETE_IP_ADDRESS:
+            {
+               SCTPDeleteIPParameter* delParam;
+               delParam = new SCTPDeleteIPParameter("DeleteIP");
+               chunkLength += SCTP_ADD_IP_PARAMETER_LENGTH;
+               delParam->setParameterType(DELETE_IP_ADDRESS);
+               delParam->setRequestCorrelationId(++state->corrIdNum);
+               delParam->setAddressParam(IPAddressResolver().resolve(sctpMain->par("addAddress"), 1));
+               if (delParam->getAddressParam().isIPv6()) {
+                  chunkLength += 20;
+                  delParam->setBitLength((SCTP_ADD_IP_PARAMETER_LENGTH+20)*8);
+               }
+               else {
+                  chunkLength += 8;
+                  delParam->setBitLength((SCTP_ADD_IP_PARAMETER_LENGTH+8)*8);
+               }
+               asconfChunk->addAsconfParam(delParam);
+               break;
+            }
+            case SET_PRIMARY_ADDRESS:
+            {
+               SCTPSetPrimaryIPParameter* priParam;
+               priParam = new SCTPSetPrimaryIPParameter("SetPrimary");
+               chunkLength += SCTP_ADD_IP_PARAMETER_LENGTH;
+               priParam->setParameterType(SET_PRIMARY_ADDRESS);
+               priParam->setRequestCorrelationId(++state->corrIdNum);
+               priParam->setAddressParam(IPAddressResolver().resolve(sctpMain->par("addAddress"), 1));
+               if (nat) {
+                  priParam->setAddressParam(IPvXAddress("0.0.0.0"));
+               }
+               if (priParam->getAddressParam().isIPv6()) {
+                  chunkLength += 20;
+                  priParam->setBitLength((SCTP_ADD_IP_PARAMETER_LENGTH+20)*8);
+               }
+               else {
+                  chunkLength += 8;
+                  priParam->setBitLength((SCTP_ADD_IP_PARAMETER_LENGTH+8)*8);
+               }
+               asconfChunk->addAsconfParam(priParam);
+               break;
+            }
+         }
+         token = strtok(NULL, ",");
+      }
+      asconfChunk->setBitLength(chunkLength*8);
+
+      if (state->auth && state->peerAuth) {
+         authChunk = createAuthChunk();
+         sctpAsconf->addChunk(authChunk);
+      }
+      sctpAsconf->addChunk(asconfChunk);
+
+      state->asconfChunk = check_and_cast<SCTPAsconfChunk*>(asconfChunk->dup());
+      state->asconfChunk->setName("STATE-ASCONF");
+
+      sendToIP(sctpAsconf, targetAddr);
+      state->asconfOutstanding = true;
+   }
+}
+
+
+void SCTPAssociation::retransmitAsconf()
+{
+   SCTPMessage* sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+
+   SCTPAsconfChunk* sctpasconf = new SCTPAsconfChunk("ASCONF-RTX");
+   sctpasconf=check_and_cast<SCTPAsconfChunk *>(state->asconfChunk->dup());
+   sctpasconf->setChunkType(ASCONF);
+   sctpasconf->setBitLength(state->asconfChunk->getBitLength());
+
+   if (state->auth && state->peerAuth) {
+      SCTPAuthenticationChunk* authChunk = createAuthChunk();
+      sctpmsg->addChunk(authChunk);
+   }
+   sctpmsg->addChunk(sctpasconf);
+
+   sendToIP(sctpmsg);
+}
+
+void SCTPAssociation::sendAsconfAck(const uint32 serialNumber)
+{
+   SCTPMessage* sctpAsconfAck = new SCTPMessage("ASCONF_ACK");
+   sctpAsconfAck->setByteLength(SCTP_COMMON_HEADER);
+   sctpAsconfAck->setSrcPort(localPort);
+   sctpAsconfAck->setDestPort(remotePort);
+
+   SCTPAsconfAckChunk* asconfAckChunk = new SCTPAsconfAckChunk("ASCONF_ACK");
+   asconfAckChunk->setChunkType(ASCONF_ACK);
+   asconfAckChunk->setSerialNumber(serialNumber);
+   asconfAckChunk->setBitLength(SCTP_ADD_IP_CHUNK_LENGTH*8);
+   if (state->auth && state->peerAuth) {
+      SCTPAuthenticationChunk* authChunk = createAuthChunk();
+      sctpAsconfAck->addChunk(authChunk);
+   }
+   sctpAsconfAck->addChunk(asconfAckChunk);
+   sendToIP(sctpAsconfAck, remoteAddr);
+}
+
+SCTPAsconfAckChunk* SCTPAssociation::createAsconfAckChunk(const uint32 serialNumber)
+{
+   SCTPAsconfAckChunk *asconfAckChunk = new SCTPAsconfAckChunk("ASCONF_ACK");
+   asconfAckChunk->setChunkType(ASCONF_ACK);
+   asconfAckChunk->setSerialNumber(serialNumber);
+   asconfAckChunk->setBitLength(SCTP_ADD_IP_CHUNK_LENGTH*8);
+   return asconfAckChunk;
+}
+
+SCTPAuthenticationChunk* SCTPAssociation::createAuthChunk()
+{
+   SCTPAuthenticationChunk* authChunk = new SCTPAuthenticationChunk("AUTH");
+
+   authChunk->setChunkType(AUTH);
+   authChunk->setSharedKey(0);
+   authChunk->setHMacIdentifier(1);
+   authChunk->setHMacOk(true);
+   authChunk->setHMACArraySize(SHA_LENGTH);
+   for (int32 i=0; i<SHA_LENGTH; i++) {
+      authChunk->setHMAC(i,0);
+   }
+   authChunk->setBitLength((SCTP_AUTH_CHUNK_LENGTH + SHA_LENGTH)*8);
+   return authChunk;
+}
+
+bool SCTPAssociation::compareRandom()
+{
+   int32 i, sizeKeyVector, sizePeerKeyVector, size;
+
+   sizeKeyVector     = sizeof(state->keyVector);
+   sizePeerKeyVector = sizeof(state->peerKeyVector);
+
+   if (sizeKeyVector != sizePeerKeyVector) {
+      if (sizePeerKeyVector > sizeKeyVector) {
+         size = sizeKeyVector;
+         for (i=sizePeerKeyVector-1; i>sizeKeyVector; i--) {
+            if (state->peerKeyVector[i]!=0)
+               return false;
+         }
+      }
+      else {
+         size = sizePeerKeyVector;
+         for (i=sizeKeyVector-1; i>sizePeerKeyVector; i--) {
+            if (state->keyVector[i]!=0)
+               return true;
+         }
+      }
+   }
+   for(i=size-1; i>0; i--) {
+      if (state->keyVector[i]<state->peerKeyVector[i])
+         return false;
+      if (state->keyVector[i]>state->peerKeyVector[i])
+         return true;
+   }
+   return true;
+}
+
+void SCTPAssociation::calculateAssocSharedKey()
+{
+   const bool peerFirst = compareRandom();
+   if (peerFirst == true) {
+      for (uint32 i=0; i<state->sizeKeyVector; i++)
+         state->sharedKey[i] = state->keyVector[i];
+      for (uint32 i=0; i<state->sizePeerKeyVector; i++)
+         state->sharedKey[i+state->sizeKeyVector] = state->peerKeyVector[i];
+   }
+   else {
+      for (uint32 i=0; i<state->sizePeerKeyVector; i++)
+         state->sharedKey[i] = state->peerKeyVector[i];
+      for (uint32 i=0; i<state->sizeKeyVector; i++)
+         state->sharedKey[i+state->sizePeerKeyVector] = state->keyVector[i];
+   }
+}
+
+bool SCTPAssociation::typeInChunkList(const uint16 type)
+{
+   for (std::vector<uint16>::iterator i=state->peerChunkList.begin(); i!=state->peerChunkList.end(); i++) {
+      if ((*i)==type) {
+         return true;
+      }
+   }
+   return false;
+}
+
+SCTPSuccessIndication* SCTPAssociation::createSuccessIndication(const uint32 correlationId)
+{
+   SCTPSuccessIndication* success = new SCTPSuccessIndication("Success");
+
+   success->setParameterType(SUCCESS_INDICATION);
+   success->setResponseCorrelationId(correlationId);
+   success->setBitLength(SCTP_ADD_IP_PARAMETER_LENGTH*8);
+   return success;
+}
+
+void SCTPAssociation::makeRoutingEntry(const char* route)
+{
+   InterfaceTableAccess interfaceTableAccess;
+   RoutingTableAccess routingTableAccess;
+   IInterfaceTable *ift = interfaceTableAccess.get();
+   if (strcmp(route,"")!=0) {
+      IPRoute* e   = new IPRoute();
+      char*    str = strtok((char*)route," ");
+
+      if (str != NULL) {
+         e->setHost(IPAddress(str));
+         str = strtok(NULL, " ");
+      }
+      if (str != NULL) {
+         e->setGateway(IPAddress(str));
+         str = strtok(NULL, " ");
+      }
+      if (str != NULL) {
+         e->setNetmask(IPAddress(str));
+         str = strtok(NULL, " ");
+      }
+      if (str != NULL) {
+         if (str[0] == 'H') {
+            e->setType(IPRoute::DIRECT);
+         } else if (str[0] == 'G')
+            e->setType(IPRoute::REMOTE);
+         str = strtok(NULL, " ");
+      }
+      if (str != NULL) {
+         e->setMetric(0);
+         str = strtok(NULL, " ");
+      }
+      if (str != NULL) {
+         opp_string(e->getInterfaceName()).reserve(500);
+         strcpy(opp_string(e->getInterfaceName()).buffer(), str);
+         e->setInterface(ift->getInterfaceByName(opp_string(e->getInterfaceName()).c_str()));
+         if (e->getInterface()==NULL)
+            opp_error("Syntax error in routing file: 6th column should be an existing interface name not `%s'", opp_string(e->getInterfaceName()).c_str());
+
+         // add entry
+         IRoutingTable *rt = routingTableAccess.get();
+         rt->addRoute(e);
+      }
+   }
+}
diff --git a/src/transport/sctp/SCTPAssociationBase.cc b/src/transport/sctp/SCTPAssociationBase.cc
index 82ab561..c5cff22 100644
--- a/src/transport/sctp/SCTPAssociationBase.cc
+++ b/src/transport/sctp/SCTPAssociationBase.cc
@@ -1,6 +1,6 @@
 //
-// Copyright (C) 2005-2010 Irene Ruengeler
-// Copyright (C) 2009-2010 Thomas Dreibholz
+// Copyright (C) 2008 Irene Ruengeler
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -30,122 +30,175 @@
 #include <sstream>
 
 
-SCTPPathVariables::SCTPPathVariables(const IPvXAddress& addr, SCTPAssociation* assoc)
+SCTPPathVariables::SCTPPathVariables(const IPvXAddress& addr,
+                                     SCTPAssociation*   assoc)
 {
-    InterfaceTableAccess interfaceTableAccess;
-
-    association                  = assoc;
-    remoteAddress                = addr;
-    activePath                   = true;
-    confirmed                    = false;
-    primaryPathCandidate         = false;
-    pathErrorCount               = 0;
-    pathErrorThreshold       = assoc->getSctpMain()->par("pathMaxRetrans");
-    if (!pathErrorThreshold) {
-        pathErrorThreshold = PATH_MAX_RETRANS;
-    }
-    pathRto                      = assoc->getSctpMain()->par("rtoInitial");
-    heartbeatTimeout             = pathRto;
-    double interval              = (double)assoc->getSctpMain()->par("hbInterval");
-    if (!interval) {
-        interval = HB_INTERVAL;
-    }
-    heartbeatIntervalTimeout = pathRto+interval;
-    srtt                         = pathRto;
-    lastAckTime                  = 0;
-    forceHb                      = false;
-    partialBytesAcked            = 0;
-    queuedBytes                  = 0;
-    outstandingBytes             = 0;
-
-    RoutingTableAccess routingTableAccess;
-    const InterfaceEntry* rtie = routingTableAccess.get()->getInterfaceForDestAddr(remoteAddress.get4());
-    if(rtie == NULL) {
-        opp_error("No interface for remote address %s found!", remoteAddress.get4().str().c_str());
-    }
-    pmtu                         = rtie->getMTU();
-    rttvar                       = 0.0;
-
-    cwndTimeout                  = pathRto;
-    cwnd                         = 0;
-    ssthresh                     = 0;
-    updateTime                   = 0.0;
-    fastRecoveryExitPoint        = 0;
-    fastRecoveryActive           = false;
-
-    numberOfFastRetransmissions      = 0;
-    numberOfTimerBasedRetransmissions = 0;
-    numberOfHeartbeatsSent = 0;
-    numberOfHeartbeatsRcvd = 0;
-    numberOfHeartbeatAcksSent = 0;
-    numberOfHeartbeatAcksRcvd = 0;
-
-    char str[128];
-    snprintf(str, sizeof(str), "HB_TIMER %d:%s",assoc->assocId,addr.str().c_str());
-    HeartbeatTimer = new cMessage(str);
-    snprintf(str, sizeof(str), "HB_INT_TIMER %d:%s",assoc->assocId,addr.str().c_str());
-    HeartbeatIntervalTimer = new cMessage(str);
-    snprintf(str, sizeof(str), "CWND_TIMER %d:%s",assoc->assocId,addr.str().c_str());
-    CwndTimer = new cMessage(str);
-    snprintf(str, sizeof(str), "RTX_TIMER %d:%s",assoc->assocId,addr.str().c_str());
-    T3_RtxTimer = new cMessage(str);
-    HeartbeatTimer->setContextPointer(association);
-    HeartbeatIntervalTimer->setContextPointer(association);
-    CwndTimer->setContextPointer(association);
-    T3_RtxTimer->setContextPointer(association);
-
-    snprintf(str, sizeof(str), "RTO %d:%s",assoc->assocId,addr.str().c_str());
-    statisticsPathRTO = new cOutVector(str);
-    snprintf(str, sizeof(str), "RTT %d:%s",assoc->assocId,addr.str().c_str());
-    statisticsPathRTT = new cOutVector(str);
-
-    snprintf(str, sizeof(str), "Slow Start Threshold %d:%s",assoc->assocId,addr.str().c_str());
-    statisticsPathSSthresh = new cOutVector(str);
-    snprintf(str, sizeof(str), "Congestion Window %d:%s",assoc->assocId,addr.str().c_str());
-    statisticsPathCwnd = new cOutVector(str);
-
-    snprintf(str, sizeof(str), "TSN Sent %d:%s",assoc->assocId,addr.str().c_str());
-    pathTSN = new cOutVector(str);
-    snprintf(str, sizeof(str), "TSN Received %d:%s",assoc->assocId,addr.str().c_str());
-    pathRcvdTSN = new cOutVector(str);
-
-    snprintf(str, sizeof(str), "HB Sent %d:%s",assoc->assocId,addr.str().c_str());
-    pathHb = new cOutVector(str);
-    snprintf(str, sizeof(str), "HB ACK Sent %d:%s",assoc->assocId,addr.str().c_str());
-    pathHbAck = new cOutVector(str);
-    snprintf(str, sizeof(str), "HB Received %d:%s",assoc->assocId,addr.str().c_str());
-    pathRcvdHb = new cOutVector(str);
-    snprintf(str, sizeof(str), "HB ACK Received %d:%s",assoc->assocId,addr.str().c_str());
-    pathRcvdHbAck = new cOutVector(str);
-
-
-
-    SCTPPathInfo* pinfo = new SCTPPathInfo("pinfo");
-    pinfo->setRemoteAddress(addr);
-    T3_RtxTimer->setControlInfo(pinfo);
-    HeartbeatTimer->setControlInfo(pinfo->dup());
-    HeartbeatIntervalTimer->setControlInfo(pinfo->dup());
-    CwndTimer->setControlInfo(pinfo->dup());
+   // ====== Path Variable Initialization ===================================
+   association              = assoc;
+   remoteAddress            = addr;
+   activePath               = true;
+   confirmed                = false;
+   primaryPathCandidate     = false;
+   pathErrorCount           = 0;
+   pathErrorThreshold       = assoc->getSctpMain()->par("pathMaxRetrans");
+   if (!pathErrorThreshold) {
+      pathErrorThreshold = PATH_MAX_RETRANS;
+   }
+   pathRto                  = assoc->getSctpMain()->par("rtoInitial");
+   heartbeatTimeout         = pathRto;
+   double interval          = (double)assoc->getSctpMain()->par("hbInterval");
+   if (!interval) {
+      interval = HB_INTERVAL;
+   }
+   heartbeatIntervalTimeout = pathRto+interval;
+   srtt                     = pathRto;
+   lastAckTime              = 0;
+   forceHb                  = false;
+   partialBytesAcked        = 0;
+   queuedBytes              = 0;
+   outstandingBytes         = 0;
+
+   RoutingTableAccess routingTableAccess;
+   const InterfaceEntry* rtie = routingTableAccess.get()->getInterfaceForDestAddr(remoteAddress.get4());
+   if(rtie == NULL) {
+      opp_error("No interface for remote address %s found!", remoteAddress.get4().str().c_str());
+   }
+   pmtu                     = rtie->getMTU();
+   rttvar                   = 0.0;
+
+   cwndTimeout              = pathRto;
+   cwnd                     = 0;
+   ssthresh                 = 0;
+   rttUpdateTime            = 0.0;
+   fastRecoveryExitPoint    = 0;
+   fastRecoveryActive       = false;
+
+   numberOfFastRetransmissions       = 0;
+   numberOfTimerBasedRetransmissions = 0;
+   numberOfDuplicates                = 0;
+
+
+   // ====== Path Info ======================================================
+   SCTPPathInfo* pinfo = new SCTPPathInfo("pinfo");
+   pinfo->setRemoteAddress(addr);
+
+
+   // ====== Timers =========================================================
+   char str[128];
+   snprintf(str, sizeof(str), "HB_TIMER %d:%s", assoc->assocId, addr.str().c_str());
+   HeartbeatTimer = new cMessage(str);
+   snprintf(str, sizeof(str), "HB_INT_TIMER %d:%s", assoc->assocId, addr.str().c_str());
+   HeartbeatIntervalTimer = new cMessage(str);
+   snprintf(str, sizeof(str), "CWND_TIMER %d:%s", assoc->assocId, addr.str().c_str());
+   CwndTimer = new cMessage(str);
+   snprintf(str, sizeof(str), "RTX_TIMER %d:%s", assoc->assocId, addr.str().c_str());
+   T3_RtxTimer = new cMessage(str);
+   snprintf(str, sizeof(str), "BLOCKING_TIMER %d:%s", assoc->assocId, addr.str().c_str());
+   BlockingTimer = new cMessage(str);
+   snprintf(str, sizeof(str), "Reset_TIMER %d:%s", assoc->assocId, addr.str().c_str());
+   ResetTimer = new cMessage(str);
+   ResetTimer->setContextPointer(association);
+   snprintf(str, sizeof(str), "ASCONF_TIMER %d:%s", assoc->assocId, addr.str().c_str());
+   AsconfTimer = new cMessage(str);
+   AsconfTimer->setContextPointer(association);
+   HeartbeatTimer->setContextPointer(association);
+   HeartbeatIntervalTimer->setContextPointer(association);
+   CwndTimer->setContextPointer(association);
+   T3_RtxTimer->setContextPointer(association);
+   T3_RtxTimer->setControlInfo(pinfo);
+   BlockingTimer->setContextPointer(association);
+   BlockingTimer->setControlInfo(pinfo->dup());
+   HeartbeatTimer->setControlInfo(pinfo->dup());
+   HeartbeatIntervalTimer->setControlInfo(pinfo->dup());
+   CwndTimer->setControlInfo(pinfo->dup());
+   ResetTimer->setControlInfo(pinfo->dup());
+   AsconfTimer->setControlInfo(pinfo->dup());
+
+   // ====== Statistics =====================================================
+   snprintf(str, sizeof(str), "RTO %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathRTO = new TimeStatsCollector(assoc->sctpMain, str, "s");
+   snprintf(str, sizeof(str), "RTT %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathRTT = new TimeStatsCollector(assoc->sctpMain, str, "s");
+
+   snprintf(str, sizeof(str), "Slow Start Threshold %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathSSthresh = new TimeStatsCollector(assoc->sctpMain, str, "Bytes");
+   snprintf(str, sizeof(str), "Congestion Window %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathCwnd = new TimeStatsCollector(assoc->sctpMain, str, "Bytes");
+   snprintf(str, sizeof(str), "Bandwidth %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathBandwidth = new TimeStatsCollector(assoc->sctpMain, str, "Bytes");
+
+   snprintf(str, sizeof(str), "TSN Sent %d:%s", assoc->assocId, addr.str().c_str());
+   vectorPathSentTSN = new cOutVector(str);
+   snprintf(str, sizeof(str), "TSN Received %d:%s", assoc->assocId, addr.str().c_str());
+   vectorPathReceivedTSN = new cOutVector(str);
+
+   snprintf(str, sizeof(str), "Queued Sent Bytes %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathQueuedSentBytes = new TimeStatsCollector(assoc->sctpMain, str, "Bytes");
+   snprintf(str, sizeof(str), "Outstanding Bytes %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathOutstandingBytes = new TimeStatsCollector(assoc->sctpMain, str, "Bytes");
+   snprintf(str, sizeof(str), "Sender Blocking Fraction %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathSenderBlockingFraction = new TimeStatsCollector(assoc->sctpMain, str, "Bytes");
+   snprintf(str, sizeof(str), "Receiver Blocking Fraction %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathReceiverBlockingFraction = new TimeStatsCollector(assoc->sctpMain, str, "Bytes");
+   snprintf(str, sizeof(str), "Number of Gap Acked Chunks in Last SACK %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathGapAckedChunksInLastSACK = new TimeStatsCollector(assoc->sctpMain, str, "Chunks");
+   snprintf(str, sizeof(str), "Number of Non-Revokable Gap Acked Chunks in Last SACK %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathGapNRAckedChunksInLastSACK = new TimeStatsCollector(assoc->sctpMain, str, "Chunks");
+   snprintf(str, sizeof(str), "Number of Gap Missed Chunks in Last SACK %d:%s", assoc->assocId, addr.str().c_str());
+   statisticsPathGapUnackedChunksInLastSACK = new TimeStatsCollector(assoc->sctpMain, str, "Chunks");
+
+   snprintf(str, sizeof(str), "Partial Bytes Acked %d:%s", assoc->assocId, addr.str().c_str());
+   vectorPathPbAcked = new cOutVector(str);
+
+   snprintf(str, sizeof(str), "Fast Recovery State %d:%s", assoc->assocId, addr.str().c_str());
+   vectorPathFastRecoveryState = new cOutVector(str);
+   vectorPathFastRecoveryState->record(0);
+
+   snprintf(str, sizeof(str), "TSN Sent Fast RTX %d:%s", assoc->assocId, addr.str().c_str());
+   vectorPathTSNFastRTX = new cOutVector(str);
+   snprintf(str, sizeof(str), "TSN Sent Timer-Based RTX %d:%s", assoc->assocId, addr.str().c_str());
+   vectorPathTSNTimerBased = new cOutVector(str);
+   snprintf(str, sizeof(str), "TSN Acked CumAck %d:%s", assoc->assocId, addr.str().c_str());
+   vectorPathAckedTSNCumAck = new cOutVector(str);
+   snprintf(str, sizeof(str), "TSN Acked GapAck %d:%s", assoc->assocId, addr.str().c_str());
+   vectorPathAckedTSNGapAck = new cOutVector(str);
 
 }
 
 SCTPPathVariables::~SCTPPathVariables()
 {
-    statisticsPathSSthresh->record(0);
-    statisticsPathCwnd->record(0);
-    delete statisticsPathSSthresh;
-    delete statisticsPathCwnd;
-    statisticsPathRTO->record(0);
-    statisticsPathRTT->record(0);
-    delete statisticsPathRTO;
-    delete statisticsPathRTT;
-
-    delete pathTSN;
-    delete pathRcvdTSN;
-    delete pathHb;
-    delete pathRcvdHb;
-    delete pathHbAck;
-    delete pathRcvdHbAck;
+   statisticsPathSSthresh->record(0);
+   statisticsPathCwnd->record(0);
+   statisticsPathBandwidth->record(0.0);
+   delete statisticsPathSSthresh;
+   delete statisticsPathCwnd;
+   delete statisticsPathBandwidth;
+   statisticsPathRTO->record(0);
+   statisticsPathRTT->record(0);
+   delete statisticsPathRTO;
+   delete statisticsPathRTT;
+
+   delete vectorPathSentTSN;
+   delete vectorPathReceivedTSN;
+
+   statisticsPathQueuedSentBytes->record(0);
+   delete statisticsPathQueuedSentBytes;
+   delete statisticsPathOutstandingBytes;
+   delete statisticsPathGapAckedChunksInLastSACK;
+   delete statisticsPathGapNRAckedChunksInLastSACK;
+   delete statisticsPathGapUnackedChunksInLastSACK;
+   delete statisticsPathSenderBlockingFraction;
+   delete statisticsPathReceiverBlockingFraction;
+
+   vectorPathPbAcked->record(0);
+   delete vectorPathPbAcked;
+   vectorPathFastRecoveryState->record(0);
+   delete vectorPathFastRecoveryState;
+
+   delete vectorPathTSNFastRTX;
+   delete vectorPathTSNTimerBased;
+   delete vectorPathAckedTSNCumAck;
+   delete vectorPathAckedTSNGapAck;
 
 }
 
@@ -154,30 +207,40 @@ const IPvXAddress SCTPDataVariables::zeroAddress = IPvXAddress("0.0.0.0");
 
 SCTPDataVariables::SCTPDataVariables()
 {
-    userData                     = NULL;
-    ordered                      = true;
-    len                          = 0;
-    tsn                          = 0;
-    sid                          = 0;
-    ssn                          = 0;
-    ppid                         = 0;
-    gapReports                   = 0;
-    enqueuingTime                = 0;
-    sendTime                     = 0;
-    ackTime                      = 0;
-    expiryTime                   = 0;
-    enqueuedInTransmissionQ      = false;
-    hasBeenAcked                 = false;
-    hasBeenReneged               = false;
-    hasBeenAbandoned             = false;
-    hasBeenFastRetransmitted     = false;
-    countsAsOutstanding          = false;
-    lastDestination              = NULL;
-    nextDestination              = NULL;
-    initialDestination           = NULL;
-    numberOfTransmissions        = 0;
-    numberOfRetransmissions      = 0;
-    booksize                     = 0;
+   userData                   = NULL;
+   ordered                    = true;
+   len                        = 0;
+   tsn                        = 0;
+   sid                        = 0;
+   ssn                        = 0;
+   ppid                       = 0;
+   fragments                  = 1;
+   gapReports                 = 0;
+   enqueuingTime              = 0;
+   sendTime                   = 0;
+   expiryTime                 = 0;
+   enqueuedInTransmissionQ    = false;
+   hasBeenAcked               = false;
+   hasBeenCountedAsNewlyAcked = false;
+   hasBeenReneged             = false;
+   hasBeenAbandoned           = false;
+   hasBeenFastRetransmitted   = false;
+   countsAsOutstanding        = false;
+   ibit                       = false;
+   queuedOnPath               = NULL;
+   ackedOnPath                = NULL;
+   hasBeenMoved               = false;
+   hasBeenTimerBasedRtxed     = false;
+   wasDropped                 = false;
+   wasPktDropped              = false;
+   firstSendTime              = 0;
+   sendForwardIfAbandoned     = false;
+   lastDestination            = NULL;
+   nextDestination            = NULL;
+   initialDestination         = NULL;
+   numberOfTransmissions      = 0;
+   numberOfRetransmissions    = 0;
+   booksize                   = 0;
 }
 
 SCTPDataVariables::~SCTPDataVariables()
@@ -186,82 +249,112 @@ SCTPDataVariables::~SCTPDataVariables()
 
 SCTPStateVariables::SCTPStateVariables()
 {
-    active                    = false;
-    fork                          = false;
-    initReceived              = false;
-    cookieEchoReceived    = false;
-    ackPointAdvanced          = false;
-    swsAvoidanceInvoked   = false;
-    firstChunkReceived    = false;
-    probingIsAllowed          = false;
-    zeroWindowProbing         = true;
-    alwaysBundleSack          = true;
-    fastRecoverySupported  = true;
-    reactivatePrimaryPath  = false;
-    newChunkReceived          = false;
-    dataChunkReceived         = false;
-    sackAllowed               = false;
-    resetPending              = false;
-    stopReceiving             = false;
-    stopOldData               = false;
-    stopSending               = false;
-    inOut                         = false;
-    queueUpdate               = false;
-    firstDataSent             = false;
-    peerWindowFull            = false;
-    zeroWindow                = false;
-    appSendAllowed            = true;
-    noMoreOutstanding         = false;
-    primaryPath               = NULL;
-    lastDataSourceAddress  = IPvXAddress("0.0.0.0");
-    shutdownChunk             = NULL;
-    initChunk                 = NULL;
-    cookieChunk               = NULL;
-    sctpmsg                   = NULL;
-    sctpMsg                   = NULL;
-    bytesToRetransmit         = 0;
-    initRexmitTimeout         = SCTP_TIMEOUT_INIT_REXMIT;
-    localRwnd                 = SCTP_DEFAULT_ARWND;
-    errorCount                = 0;
-    initRetransCounter    = 0;
-    nextTSN                   = 0;
-    cTsnAck                   = 0;
-    lastTsnAck                = 0;
-    highestTsnReceived    = 0;
-    highestTsnAcked       = 0;
-    highestTsnStored          = 0;
-    nextRSid                      = 0;
-    ackState                      = 0;
-    lastStreamScheduled   = 0;
-    peerRwnd                      = 0;
-    initialPeerRwnd       = 0;
-    assocPmtu                 = 0;
-    outstandingBytes          = 0;
-    messagesToPush            = 0;
-    pushMessagesLeft          = 0;
-    numGaps                   = 0;
-    msgNum                    = 0;
-    bytesRcvd                 = 0;
-    sendBuffer                = 0;
-    queuedReceivedBytes   = 0;
-    lastSendQueueAbated   = simTime();
-    queuedMessages            = 0;
-    queueLimit                = 0;
-    probingTimeout            = 1;
-    numRequests               = 0;
-    numMsgsReq.resize(65536);
-    for (unsigned int i = 0; i < 65536; i++) {
-        numMsgsReq[i] = 0;
-    }
-    for (unsigned int i = 0; i < MAX_GAP_COUNT; i++) {
-        gapStartList[i] = 0;
-        gapStopList[i]   = 0;
-    }
-    for (unsigned int i = 0; i < 32; i++) {
-        localTieTag[i] = 0;
-        peerTieTag[i]   = 0;
-    }
-    count = 0;
+   active                 = false;
+   fork                   = false;
+   initReceived           = false;
+   cookieEchoReceived     = false;
+   ackPointAdvanced       = false;
+   swsAvoidanceInvoked    = false;
+   firstChunkReceived     = false;
+   probingIsAllowed       = false;
+   zeroWindowProbing      = true;
+   alwaysBundleSack       = true;
+   fastRecoverySupported  = true;
+   reactivatePrimaryPath  = false;
+   newChunkReceived       = false;
+   dataChunkReceived      = false;
+   sackAllowed            = false;
+   resetPending           = false;
+   stopReceiving          = false;
+   stopOldData            = false;
+   stopSending            = false;
+   inOut                  = false;
+   asconfOutstanding      = false;
+   streamReset            = false;
+   peerStreamReset        = false;
+   queueUpdate            = false;
+   firstDataSent          = false;
+   peerWindowFull         = false;
+   zeroWindow             = false;
+   padding                = false;
+   pktDropSent            = false;
+   peerPktDrop            = false;
+   appSendAllowed         = true;
+   noMoreOutstanding      = false;
+   primaryPath            = NULL;
+   resetChunk             = NULL;
+   asconfChunk            = NULL;
+   shutdownChunk          = NULL;
+   initChunk              = NULL;
+   cookieChunk            = NULL;
+   sctpmsg                = NULL;
+   lastDataSourcePath     = NULL;
+   bytesToRetransmit      = 0;
+   chunksAdded            = 0;
+   dataChunksAdded        = 0;
+   packetBytes            = 0;
+   initRexmitTimeout      = SCTP_TIMEOUT_INIT_REXMIT;
+   localRwnd              = SCTP_DEFAULT_ARWND;
+   errorCount             = 0;
+   initRetransCounter     = 0;
+   nextTSN                = 0;
+   lastTsnAck             = 0;
+   highestTsnAcked        = 0;
+   nextRSid               = 0;
+   lastTsnBeforeReset     = 0;
+   advancedPeerAckPoint   = 0;
+   ackState               = 0;
+   lastStreamScheduled    = 0;
+   peerRwnd               = 0;
+   initialPeerRwnd        = 0;
+   assocPmtu              = 0;
+   outstandingBytes       = 0;
+   messagesToPush         = 0;
+   pushMessagesLeft       = 0;
+   msgNum                 = 0;
+   bytesRcvd              = 0;
+   sendBuffer             = 0;
+   queuedReceivedBytes    = 0;
+   assocThroughput        = 0;
+   queuedSentBytes        = 0;
+   queuedDroppableBytes   = 0;
+   lastMsgWasFragment     = false;
+   enableHeartbeats       = true;
+   sendHeartbeatsOnActivePaths = false;
+   prMethod               = 0;
+   sizeKeyVector          = 0;
+   sizePeerKeyVector      = 0;
+   auth                   = false;
+   peerAuth               = false;
+   hmacType               = 0;
+   bufferedMessages       = 0;
+   initialPeerMsgRwnd     = 0;
+   localMsgRwnd           = 0;
+   peerMsgRwnd            = 0;
+   peerAllowsChunks       = false;
+   bytesToAddPerRcvdChunk = 0;
+   bytesToAddPerPeerChunk = 0;
+   sctpMsg                = NULL;
+   tellArwnd              = false;
+   swsMsgInvoked          = false;
+   outstandingMessages    = 0;
+   ssNextStream           = false;
+   ssOneStreamLeft        = false;
+   ssLastDataChunkSizeSet = false;
+   lastSendQueueAbated    = simTime();
+   queuedMessages         = 0;
+   queueLimit             = 0;
+   probingTimeout         = 1;
+   numRequests            = 0;
+   numMsgsReq.resize(65536);
+   for (unsigned int i = 0; i < 65536; i++) {
+      numMsgsReq[i] = 0;
+   }
+   for (unsigned int i = 0; i < 32; i++) {
+      localTieTag[i] = 0;
+      peerTieTag[i]  = 0;
+   }
+   count = 0;
 }
 
 SCTPStateVariables::~SCTPStateVariables()
@@ -275,513 +368,778 @@ SCTPStateVariables::~SCTPStateVariables()
 
 SCTPAssociation::SCTPAssociation(SCTP* _module, int32 _appGateIndex, int32 _assocId)
 {
-    // ====== Initialize variables ===========================================
-    sctpMain                            = _module;
-    appGateIndex                        = _appGateIndex;
-    assocId                             = _assocId;
-    localPort                           = 0;
-    remotePort                          = 0;
-    localVTag                           = 0;
-    peerVTag                            = 0;
-    numberOfRemoteAddresses             = 0;
-    inboundStreams                      = SCTP_DEFAULT_INBOUND_STREAMS;
-    outboundStreams                     = SCTP_DEFAULT_OUTBOUND_STREAMS;
-    // queues and algorithm will be created on active or passive open
-    transmissionQ                       = NULL;
-    retransmissionQ                     = NULL;
-    sctpAlgorithm                       = NULL;
-    state                               = NULL;
-    sackPeriod                          = SACK_DELAY;
-/*
-    totalCwndAdjustmentTime         = simTime();
-    lastTotalSSthresh                   = ~0;
-    lastTotalCwnd                       = ~0;*/
-
-    cumTsnAck                           = NULL;
-    sendQueue                           = NULL;
-    numGapBlocks                        = NULL;
-
-    qCounter.roomSumSendStreams = 0;
-    qCounter.bookedSumSendStreams = 0;
-    qCounter.roomSumRcvStreams      = 0;
-    bytes.chunk                         = false;
-    bytes.packet                        = false;
-    bytes.bytesToSend                   = 0;
-
-    sctpEV3 << "SCTPAssociationBase::SCTPAssociation(): new assocId="
-              << assocId << endl;
-
-    // ====== FSM ============================================================
-    char fsmName[64];
-    snprintf(fsmName, sizeof(fsmName), "fsm-%d", assocId);
-    fsm = new cFSM();
-    fsm->setName(fsmName);
-    fsm->setState(SCTP_S_CLOSED);
-
-
-    // ====== Path Info ======================================================
-    SCTPPathInfo* pinfo = new SCTPPathInfo("pathInfo");
-    pinfo->setRemoteAddress(IPvXAddress("0.0.0.0"));
-
-    // ====== Timers =========================================================
-    char timerName[128];
-    snprintf(timerName, sizeof(timerName), "T1_INIT of Association %d", assocId);
-    T1_InitTimer = new cMessage(timerName);
-    snprintf(timerName, sizeof(timerName), "T2_SHUTDOWN of Association %d", assocId);
-    T2_ShutdownTimer = new cMessage(timerName);
-    snprintf(timerName, sizeof(timerName), "T5_SHUTDOWN_GUARD of Association %d", assocId);
-    T5_ShutdownGuardTimer = new cMessage(timerName);
-    snprintf(timerName, sizeof(timerName), "SACK_TIMER of Association %d", assocId);
-    SackTimer = new cMessage(timerName);
-
-    if (sctpMain->testTimeout > 0){
-        StartTesting = new cMessage("StartTesting");
-        StartTesting->setContextPointer(this);
-        StartTesting->setControlInfo(pinfo->dup());
-        scheduleTimeout(StartTesting, sctpMain->testTimeout);
-    }
-    T1_InitTimer->setContextPointer(this);
-    T2_ShutdownTimer->setContextPointer(this);
-    SackTimer->setContextPointer(this);
-    T5_ShutdownGuardTimer->setContextPointer(this);
-
-    T1_InitTimer->setControlInfo(pinfo);
-    T2_ShutdownTimer->setControlInfo(pinfo->dup());
-    SackTimer->setControlInfo(pinfo->dup());
-    T5_ShutdownGuardTimer->setControlInfo(pinfo->dup());
-
-    // ====== Output vectors =================================================
-    char vectorName[128];
-    snprintf(vectorName, sizeof(vectorName), "Advertised Receiver Window %d", assocId);
-    advRwnd = new cOutVector(vectorName);
-
-    // ====== Stream scheduling ==============================================
-    ssModule = sctpMain->par("ssModule");
-    switch (ssModule)
-    {
-        case ROUND_ROBIN:
-            ssFunctions.ssInitStreams    = &SCTPAssociation::initStreams;
-            ssFunctions.ssGetNextSid     = &SCTPAssociation::streamScheduler;
-            ssFunctions.ssUsableStreams  = &SCTPAssociation::numUsableStreams;
-            break;
-    }
+   // ====== Initialize variables ===========================================
+   sctpMain                      = _module;
+   appGateIndex                  = _appGateIndex;
+   assocId                       = _assocId;
+   localPort                     = 0;
+   remotePort                    = 0;
+   localVTag                     = 0;
+   peerVTag                      = 0;
+   numberOfRemoteAddresses       = 0;
+   inboundStreams                = SCTP_DEFAULT_INBOUND_STREAMS;
+   outboundStreams               = SCTP_DEFAULT_OUTBOUND_STREAMS;
+   // queues and algorithm will be created on active or passive open
+   transmissionQ                 = NULL;
+   retransmissionQ               = NULL;
+   sctpAlgorithm                 = NULL;
+   state                         = NULL;
+   sackPeriod                    = SACK_DELAY;
+
+   cumTsnAck                     = NULL;
+   sendQueue                     = NULL;
+   numGapBlocks                  = NULL;
+
+   qCounter.roomSumSendStreams   = 0;
+   qCounter.bookedSumSendStreams = 0;
+   qCounter.roomSumRcvStreams    = 0;
+   bytes.chunk                   = false;
+   bytes.packet                  = false;
+   bytes.bytesToSend             = 0;
+
+   sctpEV3 << "SCTPAssociationBase::SCTPAssociation(): new assocId="
+           << assocId << endl;
+
+   // ====== FSM ============================================================
+   char fsmName[64];
+   snprintf(fsmName, sizeof(fsmName), "fsm-%d", assocId);
+   fsm = new cFSM();
+   fsm->setName(fsmName);
+   fsm->setState(SCTP_S_CLOSED);
+
+   // ====== Chunk Map ======================================================
+   chunkMap = new ChunkMap(1024, 1024 * 1024);
+
+   // ====== Path Info ======================================================
+   SCTPPathInfo* pinfo = new SCTPPathInfo("pathInfo");
+   pinfo->setRemoteAddress(IPvXAddress("0.0.0.0"));
+
+   // ====== Timers =========================================================
+   char timerName[128];
+   snprintf(timerName, sizeof(timerName), "T1_INIT of Association %d", assocId);
+   T1_InitTimer = new cMessage(timerName);
+   snprintf(timerName, sizeof(timerName), "T2_SHUTDOWN of Association %d", assocId);
+   T2_ShutdownTimer = new cMessage(timerName);
+   snprintf(timerName, sizeof(timerName), "T5_SHUTDOWN_GUARD of Association %d", assocId);
+   T5_ShutdownGuardTimer = new cMessage(timerName);
+   snprintf(timerName, sizeof(timerName), "SACK_TIMER of Association %d", assocId);
+   SackTimer = new cMessage(timerName);
+
+   if (sctpMain->testTimeout > 0){
+      StartTesting = new cMessage("StartTesting");
+      StartTesting->setContextPointer(this);
+      StartTesting->setControlInfo(pinfo->dup());
+      scheduleTimeout(StartTesting, sctpMain->testTimeout);
+   }
+   T1_InitTimer->setContextPointer(this);
+   T2_ShutdownTimer->setContextPointer(this);
+   SackTimer->setContextPointer(this);
+   T5_ShutdownGuardTimer->setContextPointer(this);
+
+   T1_InitTimer->setControlInfo(pinfo);
+   T2_ShutdownTimer->setControlInfo(pinfo->dup());
+   SackTimer->setControlInfo(pinfo->dup());
+   T5_ShutdownGuardTimer->setControlInfo(pinfo->dup());
+
+   // ====== Output vectors =================================================
+   char vectorName[128];
+   snprintf(vectorName, sizeof(vectorName), "Advertised Receiver Window %d", assocId);
+   advRwnd = new cOutVector(vectorName);
+
+   snprintf(vectorName, sizeof(vectorName), "Slow Start Threshold %d:Total", assocId);
+   statisticsTotalSSthresh = new TimeStatsCollector(sctpMain, vectorName, "Bytes");
+   snprintf(vectorName, sizeof(vectorName), "Congestion Window %d:Total", assocId);
+   statisticsTotalCwnd = new TimeStatsCollector(sctpMain, vectorName, "Bytes");
+   snprintf(vectorName, sizeof(vectorName), "Bandwidth %d:Total", assocId);
+   statisticsTotalBandwidth = new TimeStatsCollector(sctpMain, vectorName, "Bytes");
+   snprintf(vectorName, sizeof(vectorName), "Queued Received Bytes %d:Total", assocId);
+   statisticsQueuedReceivedBytes = new TimeStatsCollector(sctpMain, vectorName, "Bytes");
+   snprintf(vectorName, sizeof(vectorName), "Queued Sent Bytes %d:Total", assocId);
+   statisticsQueuedSentBytes = new TimeStatsCollector(sctpMain, vectorName, "Bytes");
+   snprintf(vectorName, sizeof(vectorName), "Outstanding Bytes %d:Total", assocId);
+   statisticsOutstandingBytes = new TimeStatsCollector(sctpMain, vectorName, "Bytes");
+
+   snprintf(vectorName, sizeof(vectorName), "Number of Revokable Gap Blocks in SACK %d", assocId);
+   statisticsRevokableGapBlocksInLastSACK = new TimeStatsCollector(sctpMain, vectorName);
+   snprintf(vectorName, sizeof(vectorName), "Number of Non-Revokable Gap Blocks in SACK %d", assocId);
+   statisticsNonRevokableGapBlocksInLastSACK = new TimeStatsCollector(sctpMain, vectorName);
+
+   snprintf(vectorName, sizeof(vectorName), "Number of Total Gap Blocks Stored %d", assocId);
+   statisticsNumTotalGapBlocksStored = new TimeStatsCollector(sctpMain, vectorName);
+   snprintf(vectorName, sizeof(vectorName), "Number of Revokable Gap Blocks Stored %d", assocId);
+   statisticsNumRevokableGapBlocksStored = new TimeStatsCollector(sctpMain, vectorName);
+   snprintf(vectorName, sizeof(vectorName), "Number of Non-Revokable Gap Blocks Stored %d", assocId);
+   statisticsNumNonRevokableGapBlocksStored = new TimeStatsCollector(sctpMain, vectorName);
+   snprintf(vectorName, sizeof(vectorName), "Number of Duplicate TSNs Stored %d", assocId);
+   statisticsNumDuplicatesStored = new TimeStatsCollector(sctpMain, vectorName);
+
+   snprintf(vectorName, sizeof(vectorName), "Number of Revokable Gap Blocks Sent %d", assocId);
+   statisticsNumRevokableGapBlocksSent = new TimeStatsCollector(sctpMain, vectorName);
+   snprintf(vectorName, sizeof(vectorName), "Number of Non-Revokable Gap Blocks Sent %d", assocId);
+   statisticsNumNonRevokableGapBlocksSent = new TimeStatsCollector(sctpMain, vectorName);
+   snprintf(vectorName, sizeof(vectorName), "Number of Duplicate TSNs Sent %d", assocId);
+   statisticsNumDuplicatesSent = new TimeStatsCollector(sctpMain, vectorName);
+   snprintf(vectorName, sizeof(vectorName), "Length of SACK Sent %d", assocId);
+   statisticsSACKLengthSent = new TimeStatsCollector(sctpMain, vectorName);
+
+   snprintf(vectorName, sizeof(vectorName), "Arwnd in Last SACK %d", assocId);
+   statisticsArwndInLastSACK = new TimeStatsCollector(sctpMain, vectorName, "Bytes");
+   snprintf(vectorName, sizeof(vectorName), "Peer Rwnd %d", assocId);
+   statisticsPeerRwnd = new TimeStatsCollector(sctpMain, vectorName, "Bytes");
+
+   // ====== Extensions =====================================================
+   StartAddIP = new cMessage("addIP");
+   StartAddIP->setContextPointer(this);
+   StartAddIP->setControlInfo(pinfo->dup());
+   FairStartTimer = new cMessage("fairStart");
+   FairStartTimer->setContextPointer(this);
+   FairStartTimer->setControlInfo(pinfo->dup());
+   FairStopTimer = new cMessage("fairStop");
+   FairStopTimer->setContextPointer(this);
+   FairStopTimer->setControlInfo(pinfo->dup());
+   snprintf(vectorName, sizeof(vectorName), "Advertised Message Receiver Window of Association %d", assocId);
+   advMsgRwnd = new cOutVector(vectorName);
+   snprintf(vectorName, sizeof(vectorName), "End to End Delay");
+   EndToEndDelay = new cOutVector(vectorName);
+
+
+   // ====== Assoc throughput ===============================================
+   snprintf(vectorName, sizeof(vectorName), "Throughput of Association %d", assocId);
+   assocThroughputVector = new cOutVector(vectorName);
+
+   // ====== Stream scheduling ==============================================
+   ssModule = sctpMain->par("ssModule");
+   switch (ssModule)
+   {
+      case ROUND_ROBIN:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::streamScheduler;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case ROUND_ROBIN_PACKET:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::streamSchedulerRoundRobinPacket;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case RANDOM_SCHEDULE:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::streamSchedulerRandom;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case RANDOM_SCHEDULE_PACKET:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::streamSchedulerRandomPacket;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case FAIR_BANDWITH:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::streamSchedulerFairBandwidth;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case FAIR_BANDWITH_PACKET:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::streamSchedulerFairBandwidthPacket;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case PRIORITY:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::streamSchedulerPriority;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case FCFS:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::streamSchedulerFCFS;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case PATH_MANUAL:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::pathStreamSchedulerManual;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+      case PATH_MAP_TO_PATH:
+         ssFunctions.ssInitStreams   = &SCTPAssociation::initStreams;
+         ssFunctions.ssGetNextSid    = &SCTPAssociation::pathStreamSchedulerMapToPath;
+         ssFunctions.ssUsableStreams = &SCTPAssociation::numUsableStreams;
+         break;
+   }
 }
 
 SCTPAssociation::~SCTPAssociation()
 {
-    sctpEV3 << "Destructor SCTPAssociation" << endl;
-
-    delete T1_InitTimer;
-    delete T2_ShutdownTimer;
-    delete T5_ShutdownGuardTimer;
-    delete SackTimer;
-
-    delete advRwnd;
-    delete cumTsnAck;
-    delete numGapBlocks;
-    delete sendQueue;
-
-
-    delete fsm;
-    delete state;
-    delete sctpAlgorithm;
+   sctpEV3 << "Destructor SCTPAssociation" << endl;
+
+   delete T1_InitTimer;
+   delete T2_ShutdownTimer;
+   delete T5_ShutdownGuardTimer;
+   delete SackTimer;
+
+   delete advRwnd;
+   delete cumTsnAck;
+   delete numGapBlocks;
+   delete sendQueue;
+
+   delete statisticsOutstandingBytes;
+   delete statisticsQueuedReceivedBytes;
+   delete statisticsQueuedSentBytes;
+   delete statisticsTotalSSthresh;
+   delete statisticsTotalCwnd;
+   delete statisticsTotalBandwidth;
+
+   delete statisticsRevokableGapBlocksInLastSACK;
+   delete statisticsNonRevokableGapBlocksInLastSACK;
+   delete statisticsNumTotalGapBlocksStored;
+   delete statisticsNumRevokableGapBlocksStored;
+   delete statisticsNumNonRevokableGapBlocksStored;
+   delete statisticsNumDuplicatesStored;
+   delete statisticsNumRevokableGapBlocksSent;
+   delete statisticsNumNonRevokableGapBlocksSent;
+   delete statisticsNumDuplicatesSent;
+   delete statisticsSACKLengthSent;
+
+   delete statisticsArwndInLastSACK;
+   delete statisticsPeerRwnd;
+
+   delete StartAddIP;
+   delete advMsgRwnd;
+   delete EndToEndDelay;
+
+   int i = 0;
+   while (streamThroughputVectors[i] != NULL) {
+      delete streamThroughputVectors[i++];
+   }
+
+   delete assocThroughputVector;
+
+   if (FairStartTimer) delete cancelEvent(FairStartTimer);
+   if (FairStopTimer) delete cancelEvent(FairStopTimer);
+
+   delete chunkMap;
+   delete fsm;
+   delete state;
+   delete sctpAlgorithm;
 }
 
-bool SCTPAssociation::processTimer(cMessage *msg)
+bool SCTPAssociation::processTimer(cMessage* msg)
 {
-    SCTPPathVariables* path = NULL;
-
-    sctpEV3 << msg->getName() << " timer expired at "<<simulation.getSimTime()<<"\n";
-
-    SCTPPathInfo* pinfo = check_and_cast<SCTPPathInfo*>(msg->getControlInfo());
-    IPvXAddress addr = pinfo->getRemoteAddress();
-
-    if (addr != IPvXAddress("0.0.0.0"))
-        path = getPath(addr);
-     // first do actions
-    SCTPEventCode event;
-    event = SCTP_E_IGNORE;
-    if (msg==T1_InitTimer)
-    {
-        process_TIMEOUT_INIT_REXMIT(event);
-    }
-    else if (msg==SackTimer)
-    {
-    sctpEV3<<simulation.getSimTime()<<" delayed Sack: cTsnAck="<<state->cTsnAck<<" highestTsnReceived="<<state->highestTsnReceived<<" lastTsnReceived="<<state->lastTsnReceived<<" ackState="<<state->ackState<<" numGaps="<<state->numGaps<<"\n";
-        sendSack();
-    }
-    else if (msg==T2_ShutdownTimer)
-    {
-        stopTimer(T2_ShutdownTimer);
-        process_TIMEOUT_SHUTDOWN(event);
-    }
-    else if (msg==T5_ShutdownGuardTimer)
-    {
-        stopTimer(T5_ShutdownGuardTimer);
-        delete state->shutdownChunk;
-        sendIndicationToApp(SCTP_I_CONN_LOST);
-        sendAbort();
-        sctpMain->removeAssociation(this);
-    }
-    else if (path!=NULL && msg==path->HeartbeatIntervalTimer)
-    {
-        process_TIMEOUT_HEARTBEAT_INTERVAL(path, path->forceHb);
-    }
-    else if (path!=NULL && msg==path->HeartbeatTimer)
-    {
-        process_TIMEOUT_HEARTBEAT(path);
-    }
-    else if (path!=NULL && msg==path->T3_RtxTimer)
-    {
-        process_TIMEOUT_RTX(path);
-    }
-    else if (path!=NULL && msg==path->CwndTimer)
-    {
-        (this->*ccFunctions.ccUpdateAfterCwndTimeout)(path);
-    }
-    else if (strcmp(msg->getName(), "StartTesting")==0)
-    {
-        if (sctpMain->testing == false)
-        {
-            sctpMain->testing = true;
-            sctpEV3<<"set testing to true\n";
-        }
-    }
-    else
-    {
-        sctpAlgorithm->processTimer(msg, event);
-    }
-     // then state transitions
-     return performStateTransition(event);
+   SCTPPathVariables* path  = NULL;
+   SCTPPathInfo*      pinfo = check_and_cast<SCTPPathInfo*>(msg->getControlInfo());
+   IPvXAddress addr = pinfo->getRemoteAddress();
+   if (addr != IPvXAddress("0.0.0.0")) {
+      path = getPath(addr);
+   }
+
+   sctpEV3 << msg->getName() << " timer expired at " << simTime() << endl;
+
+    // first do actions
+   SCTPEventCode event;
+   event = SCTP_E_IGNORE;
+   if (msg == T1_InitTimer) {
+      process_TIMEOUT_INIT_REXMIT(event);
+   }
+   else if (msg == SackTimer) {
+      sctpEV3 << simTime() << " delayed Sack: cTsnAck=" << state->gapList.getCumAckTSN()
+              << " highestTsnReceived=" << state->gapList.getHighestTSNReceived()
+              << " lastTsnReceived="    << state->lastTsnReceived
+              << " ackState="           << state->ackState
+              << " numGaps="            << state->gapList.getNumGaps(GapList::GT_Any) << endl;
+      sendSack();
+   }
+   else if (msg == T2_ShutdownTimer) {
+      stopTimer(T2_ShutdownTimer);
+      process_TIMEOUT_SHUTDOWN(event);
+   }
+   else if (msg == T5_ShutdownGuardTimer) {
+      stopTimer(T5_ShutdownGuardTimer);
+      delete state->shutdownChunk;
+      sendIndicationToApp(SCTP_I_CONN_LOST);
+      sendAbort();
+      sctpMain->removeAssociation(this);
+   }
+   else if (path != NULL && msg == path->HeartbeatIntervalTimer) {
+      process_TIMEOUT_HEARTBEAT_INTERVAL(path, path->forceHb);
+   }
+   else if (path != NULL && msg == path->HeartbeatTimer) {
+      process_TIMEOUT_HEARTBEAT(path);
+   }
+   else if (path != NULL && msg == path->T3_RtxTimer) {
+      process_TIMEOUT_RTX(path);
+   }
+   else if (path != NULL && msg == path->BlockingTimer) {
+      process_TIMEOUT_BLOCKING(path);
+   }
+   else if (path != NULL && msg == path->CwndTimer) {
+      (this->*ccFunctions.ccUpdateAfterCwndTimeout)(path);
+   }
+   else if (strcmp(msg->getName(), "StartTesting") == 0) {
+      if (sctpMain->testing == false)
+      {
+         sctpMain->testing = true;
+         sctpEV3 << "Set testing to true" << endl;
+      }
+   }
+   else if (path != NULL && msg == path->ResetTimer) {
+      process_TIMEOUT_RESET(path);
+   }
+   else if (path != NULL && msg == path->AsconfTimer) {
+      process_TIMEOUT_ASCONF(path);
+   }
+   else if (msg == StartAddIP) {
+      state->corrIdNum = state->asconfSn;
+      const char* type = (const char *)sctpMain->par("addIpType");
+      sendAsconf(type);
+   }
+   else if (msg == FairStartTimer) {
+      SCTP::AssocStatMap::iterator it=sctpMain->assocStatMap.find(assocId);
+      if (it!=sctpMain->assocStatMap.end())
+      {
+         it->second.fairStart = simulation.getSimTime();
+         fairTimer = true;
+      }
+   }
+   else if (msg == FairStopTimer) {
+      SCTP::AssocStatMap::iterator it=sctpMain->assocStatMap.find(assocId);
+      if (it!=sctpMain->assocStatMap.end())
+      {
+         it->second.fairStop = simulation.getSimTime();
+         it->second.fairLifeTime = it->second.fairStop - it->second.fairStart;
+         it->second.fairThroughput = it->second.fairAckedBytes / it->second.fairLifeTime.dbl();
+         fairTimer = false;
+      }
+   }
+   else {
+      sctpAlgorithm->processTimer(msg, event);
+   }
+   // then state transitions
+   return performStateTransition(event);
 }
 
-bool SCTPAssociation::processSCTPMessage(SCTPMessage*           sctpmsg,
-                                                      const IPvXAddress& msgSrcAddr,
-                                                      const IPvXAddress& msgDestAddr)
+bool SCTPAssociation::processSCTPMessage(SCTPMessage*       sctpmsg,
+                                         const IPvXAddress& msgSrcAddr,
+                                         const IPvXAddress& msgDestAddr)
 {
-    printConnBrief();
+   printAssocBrief();
 
-    localAddr  = msgDestAddr;
-    localPort  = sctpmsg->getDestPort();
-    remoteAddr = msgSrcAddr;
-    remotePort = sctpmsg->getSrcPort();
+   localAddr  = msgDestAddr;
+   localPort  = sctpmsg->getDestPort();
+   remoteAddr = msgSrcAddr;
+   remotePort = sctpmsg->getSrcPort();
 
-    return process_RCV_Message(sctpmsg, msgSrcAddr, msgDestAddr);
+   return process_RCV_Message(sctpmsg, msgSrcAddr, msgDestAddr);
 }
 
 SCTPEventCode SCTPAssociation::preanalyseAppCommandEvent(int32 commandCode)
 {
-    switch (commandCode) {
-    case SCTP_C_ASSOCIATE:           return SCTP_E_ASSOCIATE;
-    case SCTP_C_OPEN_PASSIVE:        return SCTP_E_OPEN_PASSIVE;
-    case SCTP_C_SEND:                return SCTP_E_SEND;
-    case SCTP_C_CLOSE:               return SCTP_E_CLOSE;
-    case SCTP_C_ABORT:               return SCTP_E_ABORT;
-    case SCTP_C_RECEIVE:             return SCTP_E_RECEIVE;
-    case SCTP_C_SEND_UNORDERED:      return SCTP_E_SEND;
-    case SCTP_C_SEND_ORDERED:        return SCTP_E_SEND;
-    case SCTP_C_PRIMARY:             return SCTP_E_PRIMARY;
-    case SCTP_C_QUEUE_MSGS_LIMIT:    return SCTP_E_QUEUE_MSGS_LIMIT;
-    case SCTP_C_QUEUE_BYTES_LIMIT:   return SCTP_E_QUEUE_BYTES_LIMIT;
-    case SCTP_C_SHUTDOWN:            return SCTP_E_SHUTDOWN;
-    case SCTP_C_NO_OUTSTANDING:      return SCTP_E_SEND_SHUTDOWN_ACK;
-    default:
-        sctpEV3<<"commandCode="<<commandCode<<"\n";
-        opp_error("Unknown message kind in app command");
-                      return (SCTPEventCode)0; // to satisfy compiler
-    }
+   switch (commandCode) {
+   case SCTP_C_ASSOCIATE:         return SCTP_E_ASSOCIATE;
+   case SCTP_C_OPEN_PASSIVE:      return SCTP_E_OPEN_PASSIVE;
+   case SCTP_C_SEND:              return SCTP_E_SEND;
+   case SCTP_C_CLOSE:             return SCTP_E_CLOSE;
+   case SCTP_C_ABORT:             return SCTP_E_ABORT;
+   case SCTP_C_RECEIVE:           return SCTP_E_RECEIVE;
+   case SCTP_C_SEND_UNORDERED:    return SCTP_E_SEND;
+   case SCTP_C_SEND_ORDERED:      return SCTP_E_SEND;
+   case SCTP_C_PRIMARY:           return SCTP_E_PRIMARY;
+   case SCTP_C_QUEUE_MSGS_LIMIT:  return SCTP_E_QUEUE_MSGS_LIMIT;
+   case SCTP_C_QUEUE_BYTES_LIMIT: return SCTP_E_QUEUE_BYTES_LIMIT;
+   case SCTP_C_SHUTDOWN:          return SCTP_E_SHUTDOWN;
+   case SCTP_C_NO_OUTSTANDING:    return SCTP_E_SEND_SHUTDOWN_ACK;
+   case SCTP_C_STOP_SENDING:      return SCTP_E_STOP_SENDING;
+   case SCTP_C_STREAM_RESET:      return SCTP_E_STREAM_RESET;
+   case SCTP_C_SEND_ASCONF:       return SCTP_E_SEND_ASCONF;   // Needed for multihomed NAT
+   case SCTP_C_SET_STREAM_PRIO:   return SCTP_E_SET_STREAM_PRIO;
+   default:
+      sctpEV3 << "commandCode=" << commandCode << endl;
+      opp_error("Unknown message kind in app command");
+      return (SCTPEventCode)0;   // to satisfy compiler
+   }
 }
 
 bool SCTPAssociation::processAppCommand(cPacket *msg)
 {
-    printConnBrief();
-
-    // first do actions
-    SCTPCommand *sctpCommand = (SCTPCommand *)(msg->removeControlInfo());
-    SCTPEventCode event = preanalyseAppCommandEvent(msg->getKind());
-
-    sctpEV3 << "App command: " << eventName(event) << "\n";
-    switch (event)
-    {
-        case SCTP_E_ASSOCIATE: process_ASSOCIATE(event, sctpCommand, msg); break;
-        case SCTP_E_OPEN_PASSIVE: process_OPEN_PASSIVE(event, sctpCommand, msg); break;
-        case SCTP_E_SEND: process_SEND(event, sctpCommand, msg); break;
-        case SCTP_E_CLOSE: process_CLOSE(event); break;
-        case SCTP_E_ABORT: process_ABORT(event); break;
-        case SCTP_E_RECEIVE: process_RECEIVE_REQUEST(event, sctpCommand); break;
-        case SCTP_E_PRIMARY: process_PRIMARY(event, sctpCommand); break;
-        case SCTP_E_QUEUE_BYTES_LIMIT: process_QUEUE_BYTES_LIMIT(sctpCommand); break;
-        case SCTP_E_QUEUE_MSGS_LIMIT:    process_QUEUE_MSGS_LIMIT(sctpCommand); break;
-        case SCTP_E_SHUTDOWN: /*sendShutdown*/
-        sctpEV3<<"SCTP_E_SHUTDOWN in state "<<stateName(fsm->getState())<<"\n";
-            if (fsm->getState()==SCTP_S_SHUTDOWN_RECEIVED) {
-            sctpEV3<<"send shutdown ack\n";
-                sendShutdownAck(remoteAddr);
-                }
-            break;  //I.R.
-        case SCTP_E_STOP_SENDING: break;
-        case SCTP_E_SEND_SHUTDOWN_ACK:
-                /*if (fsm->getState()==SCTP_S_SHUTDOWN_RECEIVED && getOutstandingBytes()==0
-                    && qCounter.roomSumSendStreams==0 && transmissionQ->getQueueSize()==0)
-                {
-                    sendShutdownAck(state->primaryPathIndex);
-                }*/
-                break;
-        default: opp_error("wrong event code");
-    }
-    delete sctpCommand;
-    // then state transitions
-    return performStateTransition(event);
+   printAssocBrief();
+
+   // first do actions
+   SCTPCommand*  sctpCommand = (SCTPCommand*)(msg->removeControlInfo());
+   SCTPEventCode event       = preanalyseAppCommandEvent(msg->getKind());
+
+   sctpEV3 << "App command: " << eventName(event) << endl;
+   switch (event)
+   {
+      case SCTP_E_ASSOCIATE:
+         process_ASSOCIATE(event, sctpCommand, msg);
+       break;
+      case SCTP_E_OPEN_PASSIVE:
+         process_OPEN_PASSIVE(event, sctpCommand, msg);
+       break;
+      case SCTP_E_SEND:
+         process_SEND(event, sctpCommand, msg);
+        break;
+      case SCTP_E_CLOSE:
+         process_CLOSE(event);
+        break;
+      case SCTP_E_ABORT:
+         process_ABORT(event);
+        break;
+      case SCTP_E_RECEIVE:
+         process_RECEIVE_REQUEST(event, sctpCommand);
+        break;
+      case SCTP_E_PRIMARY:
+         process_PRIMARY(event, sctpCommand);
+        break;
+      case SCTP_E_STREAM_RESET:
+         if (state->peerStreamReset == true) {
+            process_STREAM_RESET(sctpCommand);
+         }
+         event = SCTP_E_IGNORE;
+        break;
+      case SCTP_E_SEND_ASCONF:
+         sendAsconf(sctpMain->par("addIpType"));
+       break;
+      case SCTP_E_SET_STREAM_PRIO:
+         state->ssPriorityMap[((SCTPSendCommand*) sctpCommand)->getSid()] =
+            ((SCTPSendCommand*) sctpCommand)->getPpid();
+         break;
+      case SCTP_E_QUEUE_BYTES_LIMIT:
+         process_QUEUE_BYTES_LIMIT(sctpCommand);
+       break;
+      case SCTP_E_QUEUE_MSGS_LIMIT:
+         process_QUEUE_MSGS_LIMIT(sctpCommand);
+       break;
+      case SCTP_E_SHUTDOWN: /*sendShutdown*/
+         sctpEV3 << "SCTP_E_SHUTDOWN in state "
+                 << stateName(fsm->getState()) << endl;
+         if (fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED) {
+            sctpEV3 << "send shutdown ack" << endl;
+            sendShutdownAck(remoteAddr);
+         }
+       break;  //I.R.
+      case SCTP_E_STOP_SENDING:
+       break;
+      case SCTP_E_SEND_SHUTDOWN_ACK:
+       break;
+      default:
+         opp_error("wrong event code");
+       break;
+   }
+   delete sctpCommand;
+   return performStateTransition(event);
 }
 
 
 bool SCTPAssociation::performStateTransition(const SCTPEventCode& event)
 {
-    sctpEV3<<"performStateTransition\n";
-    if (event==SCTP_E_IGNORE)   // e.g. discarded segment
-    {
-        ev << "Staying in state: " << stateName(fsm->getState()) << " (no FSM event)\n";
-        return true;
-    }
-
-    // state machine
-    int32 oldState = fsm->getState();
-    switch (fsm->getState())
-    {
-        case SCTP_S_CLOSED:
-            switch (event)
-            {
-                case SCTP_E_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_OPEN_PASSIVE: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_ASSOCIATE: FSM_Goto((*fsm), SCTP_S_COOKIE_WAIT); break;
-                case SCTP_E_RCV_INIT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_VALID_COOKIE_ECHO: FSM_Goto((*fsm), SCTP_S_ESTABLISHED); break;
-                    case SCTP_E_CLOSE: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                default:;
-            }
-            break;
-
-        case SCTP_S_COOKIE_WAIT:
-            switch (event)
-            {
-                case SCTP_E_RCV_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_INIT_ACK: FSM_Goto((*fsm), SCTP_S_COOKIE_ECHOED); break;
-                case SCTP_E_RCV_VALID_COOKIE_ECHO: FSM_Goto((*fsm), SCTP_S_ESTABLISHED); break;
-                default:;
-            }
-            break;
-
-        case SCTP_S_COOKIE_ECHOED:
-            switch (event)
-            {
-                case SCTP_E_RCV_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_COOKIE_ACK:FSM_Goto((*fsm), SCTP_S_ESTABLISHED); break;
-                default:;
-            }
-            break;
-        case SCTP_S_ESTABLISHED:
-            switch (event)
-            {
-                case SCTP_E_SEND: FSM_Goto((*fsm), SCTP_S_ESTABLISHED); break;
-                case SCTP_E_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_SHUTDOWN: FSM_Goto((*fsm), SCTP_S_SHUTDOWN_PENDING);    break;
-                case SCTP_E_STOP_SENDING: FSM_Goto((*fsm), SCTP_S_SHUTDOWN_PENDING); state->stopSending = true; state->lastTSN = state->nextTSN-1; break;    //I.R.
-                case SCTP_E_RCV_SHUTDOWN: FSM_Goto((*fsm), SCTP_S_SHUTDOWN_RECEIVED); break;
-                case SCTP_E_CLOSE: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                default:;
-            }
-            break;
-
-        case SCTP_S_SHUTDOWN_PENDING:
-            switch (event)
-            {
-                case SCTP_E_RCV_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_NO_MORE_OUTSTANDING: FSM_Goto((*fsm), SCTP_S_SHUTDOWN_SENT); break;
-                case SCTP_E_RCV_SHUTDOWN: FSM_Goto((*fsm), SCTP_S_SHUTDOWN_RECEIVED); break;
-                case SCTP_E_RCV_SHUTDOWN_ACK: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                default:;
-            }
-            break;
-
-        case SCTP_S_SHUTDOWN_RECEIVED:
-            switch (event)
-            {
-                case SCTP_E_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_NO_MORE_OUTSTANDING:
-                            FSM_Goto((*fsm), SCTP_S_SHUTDOWN_ACK_SENT);
-                    break;
-                case SCTP_E_SHUTDOWN: sendShutdownAck(remoteAddr); /*FSM_Goto((*fsm), SCTP_S_SHUTDOWN_ACK_SENT);*/ break;
-                default:;
-            }
-            break;
-
-        case SCTP_S_SHUTDOWN_SENT:
-            switch (event)
-            {
-                case SCTP_E_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_SHUTDOWN_ACK: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_SHUTDOWN: sendShutdownAck(remoteAddr); FSM_Goto((*fsm), SCTP_S_SHUTDOWN_ACK_SENT); break;
-                default:;
-            }
-            break;
-
-        case SCTP_S_SHUTDOWN_ACK_SENT:
-            switch (event)
-            {
-                case SCTP_E_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_ABORT: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                case SCTP_E_RCV_SHUTDOWN_COMPLETE:      FSM_Goto((*fsm), SCTP_S_CLOSED); break;
-                default:;
-            }
-            break;
-
-    }
-
-    if (oldState!=fsm->getState())
-    {
-        ev << "Transition: " << stateName(oldState) << " --> " << stateName(fsm->getState()) << "    (event was: " << eventName(event) << ")\n";
-        sctpEV3 << sctpMain->getName() << ": " << stateName(oldState) << " --> " << stateName(fsm->getState()) << "  (on " << eventName(event) << ")\n";
-        stateEntered(fsm->getState());
-    }
-    else
-    {
-        ev<< "Staying in state: " << stateName(fsm->getState()) << " (event was: " << eventName(event) << ")\n";
-    }
-    if (event==SCTP_E_ABORT && oldState==fsm->getState() && fsm->getState()==SCTP_S_CLOSED)
-        return true;
-
-    if (oldState!=fsm->getState() && fsm->getState()==SCTP_S_CLOSED)
-    {
-        sctpEV3<<"return false because oldState="<<oldState<<" and new state is closed\n";
-        return false;
-    }
-    else
-        return true;
+   sctpEV3 << "performStateTransition()" << endl;
+   if (event == SCTP_E_IGNORE) {   // e.g. discarded segment
+      sctpEV3 << "Staying in state: " << stateName(fsm->getState())
+              << " (no FSM event)" << endl;
+      return true;
+   }
+
+   // state machine
+   const int32 oldState = fsm->getState();
+   switch (fsm->getState())
+   {
+      case SCTP_S_CLOSED:
+         switch (event) {
+            case SCTP_E_ABORT:                 FSM_Goto((*fsm), SCTP_S_CLOSED);      break;
+            case SCTP_E_OPEN_PASSIVE:          FSM_Goto((*fsm), SCTP_S_CLOSED);      break;
+            case SCTP_E_ASSOCIATE:             FSM_Goto((*fsm), SCTP_S_COOKIE_WAIT); break;
+            case SCTP_E_RCV_INIT:              FSM_Goto((*fsm), SCTP_S_CLOSED);      break;
+            case SCTP_E_RCV_ABORT:             FSM_Goto((*fsm), SCTP_S_CLOSED);      break;
+            case SCTP_E_RCV_VALID_COOKIE_ECHO: FSM_Goto((*fsm), SCTP_S_ESTABLISHED); break;
+            case SCTP_E_CLOSE:                 FSM_Goto((*fsm), SCTP_S_CLOSED);      break;
+            default:;
+         }
+         break;
+
+      case SCTP_S_COOKIE_WAIT:
+         switch (event) {
+            case SCTP_E_RCV_ABORT:             FSM_Goto((*fsm), SCTP_S_CLOSED);        break;
+            case SCTP_E_ABORT:                 FSM_Goto((*fsm), SCTP_S_CLOSED);        break;
+            case SCTP_E_RCV_INIT_ACK:          FSM_Goto((*fsm), SCTP_S_COOKIE_ECHOED); break;
+            case SCTP_E_RCV_VALID_COOKIE_ECHO: FSM_Goto((*fsm), SCTP_S_ESTABLISHED);   break;
+            default:;
+         }
+         break;
+
+      case SCTP_S_COOKIE_ECHOED:
+         switch (event) {
+            case SCTP_E_RCV_ABORT:             FSM_Goto((*fsm), SCTP_S_CLOSED);      break;
+            case SCTP_E_ABORT:                 FSM_Goto((*fsm), SCTP_S_CLOSED);      break;
+            case SCTP_E_RCV_COOKIE_ACK:        FSM_Goto((*fsm), SCTP_S_ESTABLISHED); break;
+            default:;
+         }
+         break;
+
+      case SCTP_S_ESTABLISHED:
+         switch (event) {
+            case SCTP_E_SEND:                  FSM_Goto((*fsm), SCTP_S_ESTABLISHED);       break;
+            case SCTP_E_ABORT:                 FSM_Goto((*fsm), SCTP_S_CLOSED);            break;
+            case SCTP_E_RCV_ABORT:             FSM_Goto((*fsm), SCTP_S_CLOSED);            break;
+            case SCTP_E_SHUTDOWN:              FSM_Goto((*fsm), SCTP_S_SHUTDOWN_PENDING);  break;
+            case SCTP_E_STOP_SENDING:          FSM_Goto((*fsm), SCTP_S_SHUTDOWN_PENDING);
+               state->stopSending = true;
+               state->lastTSN     = state->nextTSN - 1;
+             break;  //I.R.
+            case SCTP_E_RCV_SHUTDOWN:          FSM_Goto((*fsm), SCTP_S_SHUTDOWN_RECEIVED); break;
+            case SCTP_E_CLOSE:                 FSM_Goto((*fsm), SCTP_S_CLOSED);            break;
+            default:
+             break;
+         }
+         break;
+
+      case SCTP_S_SHUTDOWN_PENDING:
+         switch (event) {
+            case SCTP_E_RCV_ABORT:             FSM_Goto((*fsm), SCTP_S_CLOSED);            break;
+            case SCTP_E_ABORT:                 FSM_Goto((*fsm), SCTP_S_CLOSED);            break;
+            case SCTP_E_NO_MORE_OUTSTANDING:   FSM_Goto((*fsm), SCTP_S_SHUTDOWN_SENT);     break;
+            case SCTP_E_RCV_SHUTDOWN:          FSM_Goto((*fsm), SCTP_S_SHUTDOWN_RECEIVED); break;
+            case SCTP_E_RCV_SHUTDOWN_ACK:      FSM_Goto((*fsm), SCTP_S_CLOSED);            break;
+            default:;
+         }
+         break;
+
+      case SCTP_S_SHUTDOWN_RECEIVED:
+         switch (event) {
+            case SCTP_E_ABORT:                 FSM_Goto((*fsm), SCTP_S_CLOSED); break;
+            case SCTP_E_RCV_ABORT:             FSM_Goto((*fsm), SCTP_S_CLOSED); break;
+            case SCTP_E_NO_MORE_OUTSTANDING:
+               FSM_Goto((*fsm), SCTP_S_SHUTDOWN_ACK_SENT);
+             break;
+            case SCTP_E_SHUTDOWN:
+               sendShutdownAck(remoteAddr);
+               break;
+            default:
+             break;
+         }
+         break;
+
+      case SCTP_S_SHUTDOWN_SENT:
+         switch (event)
+         {
+            case SCTP_E_ABORT:                 FSM_Goto((*fsm), SCTP_S_CLOSED); break;
+            case SCTP_E_RCV_ABORT:             FSM_Goto((*fsm), SCTP_S_CLOSED); break;
+            case SCTP_E_RCV_SHUTDOWN_ACK:      FSM_Goto((*fsm), SCTP_S_CLOSED); break;
+            case SCTP_E_RCV_SHUTDOWN:
+               sendShutdownAck(remoteAddr);
+               FSM_Goto((*fsm), SCTP_S_SHUTDOWN_ACK_SENT);
+             break;
+            default:
+             break;
+         }
+         break;
+
+      case SCTP_S_SHUTDOWN_ACK_SENT:
+         switch (event)
+         {
+            case SCTP_E_ABORT:                 FSM_Goto((*fsm), SCTP_S_CLOSED); break;
+            case SCTP_E_RCV_ABORT:             FSM_Goto((*fsm), SCTP_S_CLOSED); break;
+            case SCTP_E_RCV_SHUTDOWN_COMPLETE: FSM_Goto((*fsm), SCTP_S_CLOSED); break;
+            default:
+             break;
+         }
+         break;
+   }
+
+   if (oldState != fsm->getState()) {
+      ev << "Transition: " << stateName(oldState) << " --> "
+         << stateName(fsm->getState())
+         << " (event was: " << eventName(event) << ")" << endl;
+      sctpEV3 << sctpMain->getName() << ": "
+              << stateName(oldState) << " --> " << stateName(fsm->getState())
+              << " (on " << eventName(event) << ")" << endl;
+      stateEntered(fsm->getState());
+   }
+   else {
+      ev << "Staying in state: " << stateName(fsm->getState())
+         << " (event was: " << eventName(event) << ")" << endl;
+   }
+   if ((event == SCTP_E_ABORT) &&
+       (oldState == fsm->getState()) &&
+       (fsm->getState() == SCTP_S_CLOSED)) {
+      return true;
+   }
+   if ( (oldState != fsm->getState()) && (fsm->getState() == SCTP_S_CLOSED) ) {
+      sctpEV3 << "returning false because oldState=" << oldState
+              << " and new state is closed" << endl;
+      return false;
+   }
+   else
+      return true;
 }
 
 void SCTPAssociation::stateEntered(int32 status)
 {
-    switch (status)
-    {
-        case SCTP_S_COOKIE_WAIT:
-            break;
-        case SCTP_S_ESTABLISHED:
-        {
-            sctpEV3 << "State ESTABLISHED entered" << endl;
-            stopTimer(T1_InitTimer);
-            if (state->initChunk) {
-                delete state->initChunk;
-            }
-            state->nagleEnabled                   = (bool)sctpMain->par("nagleEnabled");
-            state->enableHeartbeats               = (bool)sctpMain->par("enableHeartbeats");
-            state->numGapReports                  = sctpMain->par("numGapReports");
-            state->maxBurst                       = (uint32)sctpMain->par("maxBurst");
-                state->header = 0;
-            state->swsLimit                      = (uint32)sctpMain->par("swsLimit");
-            state->fastRecoverySupported         = (bool)sctpMain->par("fastRecoverySupported");
-            state->reactivatePrimaryPath         = (bool)sctpMain->par("reactivatePrimaryPath");
-            sackPeriod                           = (double)sctpMain->par("sackPeriod");
-            sackFrequency                        = sctpMain->par("sackFrequency");
-            SCTP::AssocStat stat;
-            stat.assocId                         = assocId;
-            stat.start                           = simulation.getSimTime();
-            stat.stop                            = 0;
-            stat.rcvdBytes                       = 0;
-            stat.ackedBytes                      = 0;
-            stat.sentBytes                       = 0;
-            stat.transmittedBytes                = 0;
-            stat.numFastRtx                      = 0;
-            stat.numT3Rtx                        = 0;
-            stat.numDups                         = 0;
-            stat.numPathFailures                 = 0;
-            stat.numForwardTsn                   = 0;
-            stat.lifeTime                        = 0;
-            stat.throughput                      = 0;
-            sctpMain->assocStatMap[stat.assocId] = stat;
-            ccModule = sctpMain->par("ccModule");
-            switch (ccModule)
+   switch (status)
+   {
+      case SCTP_S_COOKIE_WAIT:
+         break;
+      case SCTP_S_ESTABLISHED:
+      {
+         sctpEV3 << "State ESTABLISHED entered" << endl;
+         stopTimer(T1_InitTimer);
+         if (state->initChunk) {
+            delete state->initChunk;
+         }
+         state->nagleEnabled                = (bool)sctpMain->par("nagleEnabled");
+         state->enableHeartbeats            = (bool)sctpMain->par("enableHeartbeats");
+         state->sendHeartbeatsOnActivePaths = (bool)sctpMain->par("sendHeartbeatsOnActivePaths");
+         state->numGapReports               = sctpMain->par("numGapReports");
+         state->maxBurst                    = (uint32)sctpMain->par("maxBurst");
+         state->rtxMethod                   = sctpMain->par("RTXMethod");
+         state->nrSack                      = (bool)sctpMain->par("nrSack");
+         state->disableReneging             = (bool)sctpMain->par("disableReneging");
+         state->checkSackSeqNumber          = (bool)sctpMain->par("checkSackSeqNumber");
+         state->outgoingSackSeqNum          = 0;
+         state->incomingSackSeqNum          = 0;
+
+         state->osbWithHeader               = (bool)sctpMain->par("osbWithHeader");
+         state->padding                     = (bool)sctpMain->par("padding");
+         if (state->osbWithHeader)
+            state->header = SCTP_DATA_CHUNK_LENGTH;
+         else
+            state->header = 0;
+         state->swsLimit                   = (uint32)sctpMain->par("swsLimit");
+         state->fastRecoverySupported      = (bool)sctpMain->par("fastRecoverySupported");
+         state->reactivatePrimaryPath      = (bool)sctpMain->par("reactivatePrimaryPath");
+         state->packetsInTotalBurst        = 0;
+         state->auth                       = sctpMain->auth;
+         state->messageAcceptLimit         = sctpMain->par("messageAcceptLimit");
+         state->bytesToAddPerRcvdChunk     = sctpMain->par("bytesToAddPerRcvdChunk");
+         state->bytesToAddPerPeerChunk     = sctpMain->par("bytesToAddPerPeerChunk");
+         state->tellArwnd                  = sctpMain->par("tellArwnd");
+         state->throughputInterval         = (double)sctpMain->par("throughputInterval");
+         sackPeriod                        = (double)sctpMain->par("sackPeriod");
+         sackFrequency                     = sctpMain->par("sackFrequency");
+         SCTP::AssocStat stat;
+         stat.assocId                      = assocId;
+         stat.start                        = simulation.getSimTime();
+         stat.stop                         = 0;
+         stat.rcvdBytes                    = 0;
+         stat.ackedBytes                   = 0;
+         stat.sentBytes                    = 0;
+         stat.transmittedBytes             = 0;
+         stat.numFastRtx                   = 0;
+         stat.numT3Rtx                     = 0;
+         stat.numDups                      = 0;
+         stat.numPathFailures              = 0;
+         stat.numForwardTsn                = 0;
+         stat.sumRGapRanges                = 0;
+         stat.sumNRGapRanges               = 0;
+         stat.numOverfullSACKs             = 0;
+         stat.lifeTime                     = 0;
+         stat.throughput                   = 0;
+         stat.numDropsBecauseNewTSNGreaterThanHighestTSN = 0;
+         stat.numDropsBecauseNoRoomInBuffer              = 0;
+         stat.numChunksReneged                           = 0;
+         fairTimer                         = false;
+         stat.fairStart                    = 0;
+         stat.fairStop                     = 0;
+         stat.fairLifeTime                 = 0;
+         stat.fairThroughput               = 0;
+         stat.fairAckedBytes               = 0;
+         stat.numEndToEndMessages          = 0;
+         stat.cumEndToEndDelay             = 0;
+         stat.startEndToEndDelay           = (uint32)sctpMain->par("startEndToEndDelay");
+         stat.stopEndToEndDelay            = (uint32)sctpMain->par("stopEndToEndDelay");
+         sctpMain->assocStatMap[stat.assocId] = stat;
+         ccModule = sctpMain->par("ccModule");
+         switch (ccModule)
+         {
+            case RFC4960:
             {
-                case RFC4960:
-                {
-                    ccFunctions.ccInitParams                 = &SCTPAssociation::initCCParameters;
-                    ccFunctions.ccUpdateAfterSack            = &SCTPAssociation::cwndUpdateAfterSack;
-                    ccFunctions.ccUpdateAfterCwndTimeout     = &SCTPAssociation::cwndUpdateAfterCwndTimeout;
-                    ccFunctions.ccUpdateAfterRtxTimeout      = &SCTPAssociation::cwndUpdateAfterRtxTimeout;
-                    ccFunctions.ccUpdateMaxBurst             = &SCTPAssociation::cwndUpdateMaxBurst;
-                    ccFunctions.ccUpdateBytesAcked           = &SCTPAssociation::cwndUpdateBytesAcked;
-                    break;
-                }
-            }
-            pmStartPathManagement();
-            state->sendQueueLimit = (uint32)sctpMain->par("sendQueueLimit");
-            sendEstabIndicationToApp();
-            char str[128];
-            snprintf(str, sizeof(str), "Cumulated TSN Ack of Association %d", assocId);
-            cumTsnAck = new cOutVector(str);
-            snprintf(str, sizeof(str), "Number of Gap Blocks in Last SACK of Association %d", assocId);
-            numGapBlocks = new cOutVector(str);
-            snprintf(str, sizeof(str), "SendQueue of Association %d", assocId);
-            sendQueue = new cOutVector(str);
-            state->sendQueueLimit = (uint32)sctpMain->par("sendQueueLimit");
-            SCTP::VTagPair vtagPair;
-            vtagPair.peerVTag     = peerVTag;
-            vtagPair.localVTag  = localVTag;
-            vtagPair.localPort  = localPort;
-            vtagPair.remotePort = remotePort;
-            sctpMain->sctpVTagMap[assocId] = vtagPair;
-            break;
-        }
-        case SCTP_S_CLOSED:
-        {
-            sendIndicationToApp(SCTP_I_CLOSED);
-            break;
-        }
-        case SCTP_S_SHUTDOWN_PENDING:
-        {
-            if (getOutstandingBytes()==0 && transmissionQ->getQueueSize()==0 && qCounter.roomSumSendStreams==0)
-                sendShutdown();
-            break;
-        }
-        case SCTP_S_SHUTDOWN_RECEIVED:
-        {
-            sctpEV3 << "Entered state SHUTDOWN_RECEIVED, osb=" << getOutstandingBytes()
-                      << ", transQ=" << transmissionQ->getQueueSize()
-                      << ", scount=" << qCounter.roomSumSendStreams << endl;
-            if (getOutstandingBytes()==0 && transmissionQ->getQueueSize()==0 && qCounter.roomSumSendStreams==0) {
-                sendShutdownAck(remoteAddr);
-            }
-            else {
-                sendOnAllPaths(state->getPrimaryPath());
+               ccFunctions.ccInitParams             = &SCTPAssociation::initCCParameters;
+               ccFunctions.ccUpdateBeforeSack       = &SCTPAssociation::cwndUpdateBeforeSack;
+               ccFunctions.ccUpdateAfterSack        = &SCTPAssociation::cwndUpdateAfterSack;
+               ccFunctions.ccUpdateAfterCwndTimeout = &SCTPAssociation::cwndUpdateAfterCwndTimeout;
+               ccFunctions.ccUpdateAfterRtxTimeout  = &SCTPAssociation::cwndUpdateAfterRtxTimeout;
+               ccFunctions.ccUpdateMaxBurst         = &SCTPAssociation::cwndUpdateMaxBurst;
+               ccFunctions.ccUpdateBytesAcked       = &SCTPAssociation::cwndUpdateBytesAcked;
+               break;
             }
-            break;
-        }
-    }
+         }
+         pmStartPathManagement();
+         state->sendQueueLimit = (uint32)sctpMain->par("sendQueueLimit");
+         sendEstabIndicationToApp();
+         const bool addIP = (bool)sctpMain->par("addIP");
+         sctpEV3<<getFullPath()<<": addIP = "<<addIP<<" time = "<<(double)sctpMain->par("addTime")<<"\n";
+         if (addIP == true && (double)sctpMain->par("addTime")>0)
+         {
+            sctpEV3<<"startTimer addTime to expire at "<<simulation.getSimTime()+(double)sctpMain->par("addTime")<<"\n";
+
+            scheduleTimeout(StartAddIP, (double)sctpMain->par("addTime"));
+         }
+         if ((double)sctpMain->par("fairStart")>0)
+         {
+            sctpMain->scheduleAt(sctpMain->par("fairStart"), FairStartTimer);
+            sctpMain->scheduleAt(sctpMain->par("fairStop"), FairStopTimer);
+            sctpMain->recordScalar("rtoMin",(double)sctpMain->par("rtoMin"));
+         }
+         char str[128];
+         snprintf(str, sizeof(str), "Cumulated TSN Ack of Association %d", assocId);
+         cumTsnAck = new cOutVector(str);
+         snprintf(str, sizeof(str), "Number of Gap Blocks in Last SACK of Association %d", assocId);
+         numGapBlocks = new cOutVector(str);
+         snprintf(str, sizeof(str), "SendQueue of Association %d", assocId);
+         sendQueue = new cOutVector(str);
+         state->sendQueueLimit = (uint32)sctpMain->par("sendQueueLimit");
+         SCTP::VTagPair vtagPair;
+         vtagPair.peerVTag   = peerVTag;
+         vtagPair.localVTag  = localVTag;
+         vtagPair.localPort  = localPort;
+         vtagPair.remotePort = remotePort;
+         sctpMain->sctpVTagMap[assocId] = vtagPair;
+         break;
+      }
+      case SCTP_S_CLOSED:
+      {
+         sendIndicationToApp(SCTP_I_CLOSED);
+         break;
+      }
+      case SCTP_S_SHUTDOWN_PENDING:
+      {
+         if (getOutstandingBytes()==0 && transmissionQ->getQueueSize()==0 && qCounter.roomSumSendStreams==0)
+            sendShutdown();
+         break;
+      }
+      case SCTP_S_SHUTDOWN_RECEIVED:
+      {
+         sctpEV3 << "Entered state SHUTDOWN_RECEIVED, osb=" << getOutstandingBytes()
+                 << ", transQ=" << transmissionQ->getQueueSize()
+                 << ", scount=" << qCounter.roomSumSendStreams << endl;
+         if (getOutstandingBytes()==0 && transmissionQ->getQueueSize()==0 && qCounter.roomSumSendStreams==0) {
+            sendShutdownAck(remoteAddr);
+         }
+         else {
+            sendOnAllPaths(state->getPrimaryPath());
+         }
+         break;
+      }
+   }
 }
 
 void SCTPAssociation::removePath()
 {
-    SCTPPathMap::iterator pathIterator;
-    while((pathIterator = sctpPathMap.begin()) != sctpPathMap.end())
-    {
-        SCTPPathVariables* path = pathIterator->second;
-        sctpEV3 << getFullPath() << " remove path " << path->remoteAddress << endl;
-        stopTimer(path->HeartbeatTimer);
-        delete path->HeartbeatTimer;
-        stopTimer(path->HeartbeatIntervalTimer);
-        sctpEV3 << "delete timer " << path->HeartbeatIntervalTimer->getName() << endl;
-        delete path->HeartbeatIntervalTimer;
-        stopTimer(path->T3_RtxTimer);
-        delete path->T3_RtxTimer;
-        stopTimer(path->CwndTimer);
-        delete path->CwndTimer;
-        delete path;
-        sctpPathMap.erase(pathIterator);
-    }
+   SCTPPathMap::iterator pathIterator;
+   while((pathIterator = sctpPathMap.begin()) != sctpPathMap.end())
+   {
+      SCTPPathVariables* path = pathIterator->second;
+      sctpEV3 << getFullPath() << " remove path " << path->remoteAddress << endl;
+      stopTimer(path->HeartbeatTimer);
+      delete path->HeartbeatTimer;
+      stopTimer(path->HeartbeatIntervalTimer);
+      delete path->HeartbeatIntervalTimer;
+      stopTimer(path->T3_RtxTimer);
+      delete path->T3_RtxTimer;
+      stopTimer(path->BlockingTimer);
+      delete path->BlockingTimer;
+      stopTimer(path->CwndTimer);
+      delete path->CwndTimer;
+      stopTimer(path->ResetTimer);
+      delete path->ResetTimer;
+      stopTimer(path->AsconfTimer);
+      delete path->AsconfTimer;
+      delete path;
+      sctpPathMap.erase(pathIterator);
+   }
 }
diff --git a/src/transport/sctp/SCTPAssociationEventProc.cc b/src/transport/sctp/SCTPAssociationEventProc.cc
index a07ad9f..adae99c 100644
--- a/src/transport/sctp/SCTPAssociationEventProc.cc
+++ b/src/transport/sctp/SCTPAssociationEventProc.cc
@@ -1,6 +1,6 @@
 //
-// Copyright (C) 2005-2010 by Irene Ruengeler
-// Copyright (C) 2009-2010 by Thomas Dreibholz
+// Copyright (C) 2008 by Irene Ruengeler
+// Copyright (C) 2009-2012 by Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -30,120 +30,123 @@
 
 void SCTPAssociation::process_ASSOCIATE(SCTPEventCode& event, SCTPCommand *sctpCommand, cPacket *msg)
 {
-    IPvXAddress lAddr, rAddr;
-
-    SCTPOpenCommand *openCmd = check_and_cast<SCTPOpenCommand *>(sctpCommand);
-
-    ev<<"SCTPAssociationEventProc:process_ASSOCIATE\n";
-
-    switch(fsm->getState())
-    {
-        case SCTP_S_CLOSED:
-        initAssociation(openCmd);
-        state->active = true;
-        localAddressList = openCmd->getLocalAddresses();
-        lAddr = openCmd->getLocalAddresses().front();
-        if (!(openCmd->getRemoteAddresses().empty()))
-        {
-            remoteAddressList = openCmd->getRemoteAddresses();
-            rAddr = openCmd->getRemoteAddresses().front();
-        }
-        else
-            rAddr = openCmd->getRemoteAddr();
-        localPort = openCmd->getLocalPort();
-        remotePort = openCmd->getRemotePort();
-        state->numRequests = openCmd->getNumRequests();
-        if (rAddr.isUnspecified() || remotePort==0)
-        opp_error("Error processing command OPEN_ACTIVE: remote address and port must be specified");
-
-        if (localPort==0)
-        {
-        localPort = sctpMain->getEphemeralPort();
-        }
-        ev << "OPEN: " << lAddr << ":" << localPort << " --> " << rAddr << ":" << remotePort << "\n";
-
-        sctpMain->updateSockPair(this, lAddr, rAddr, localPort, remotePort);
-        state->localRwnd = (long)sctpMain->par("arwnd");
-        sendInit();
-        startTimer(T1_InitTimer,state->initRexmitTimeout);
-        break;
-
-    default:
-        opp_error("Error processing command OPEN_ACTIVE: connection already exists");
-    }
+   IPvXAddress lAddr, rAddr;
+
+   SCTPOpenCommand *openCmd = check_and_cast<SCTPOpenCommand *>(sctpCommand);
+
+   ev<<"SCTPAssociationEventProc:process_ASSOCIATE\n";
+
+   switch(fsm->getState())
+   {
+      case SCTP_S_CLOSED:
+      initAssociation(openCmd);
+      state->active = true;
+      localAddressList = openCmd->getLocalAddresses();
+      lAddr = openCmd->getLocalAddresses().front();
+      if (!(openCmd->getRemoteAddresses().empty()))
+      {
+         remoteAddressList = openCmd->getRemoteAddresses();
+         rAddr = openCmd->getRemoteAddresses().front();
+      }
+      else
+         rAddr = openCmd->getRemoteAddr();
+      localPort = openCmd->getLocalPort();
+      remotePort = openCmd->getRemotePort();
+      state->streamReset = openCmd->getStreamReset();
+      state->prMethod = openCmd->getPrMethod();
+      state->numRequests = openCmd->getNumRequests();
+      if (rAddr.isUnspecified() || remotePort==0)
+      opp_error("Error processing command OPEN_ACTIVE: remote address and port must be specified");
+
+      if (localPort==0)
+      {
+      localPort = sctpMain->getEphemeralPort();
+      }
+      ev << "OPEN: " << lAddr << ":" << localPort << " --> " << rAddr << ":" << remotePort << "\n";
+
+      sctpMain->updateSockPair(this, lAddr, rAddr, localPort, remotePort);
+      state->localRwnd = (long)sctpMain->par("arwnd");
+      state->localMsgRwnd = sctpMain->par("messageAcceptLimit");
+      sendInit();
+      startTimer(T1_InitTimer,state->initRexmitTimeout);
+      break;
+
+   default:
+      opp_error("Error processing command OPEN_ACTIVE: connection already exists");
+   }
 
 }
 
 void SCTPAssociation::process_OPEN_PASSIVE(SCTPEventCode& event, SCTPCommand *sctpCommand, cPacket *msg)
 {
-    IPvXAddress lAddr;
-    int16 localPort;
-
-    SCTPOpenCommand *openCmd = check_and_cast<SCTPOpenCommand *>(sctpCommand);
-
-    sctpEV3<<"SCTPAssociationEventProc:process_OPEN_PASSIVE\n";
-
-    switch(fsm->getState())
-    {
-        case SCTP_S_CLOSED:
-            initAssociation(openCmd);
-            state->fork = openCmd->getFork();
-            localAddressList = openCmd->getLocalAddresses();
-            sctpEV3<<"process_OPEN_PASSIVE: number of local addresses="<<localAddressList.size()<<"\n";
-            lAddr = openCmd->getLocalAddresses().front();
-            localPort = openCmd->getLocalPort();
-            inboundStreams = openCmd->getInboundStreams();
-            outboundStreams = openCmd->getOutboundStreams();
-            state->localRwnd = (long)sctpMain->par("arwnd");
-            state->numRequests = openCmd->getNumRequests();
-            state->messagesToPush = openCmd->getMessagesToPush();
-
-            if (localPort==0)
-                opp_error("Error processing command OPEN_PASSIVE: local port must be specified");
-            sctpEV3 << "Assoc "<<assocId<<"::Starting to listen on: " << lAddr << ":" << localPort << "\n";
-
-            sctpMain->updateSockPair(this, lAddr, IPvXAddress(), localPort, 0);
-            break;
-        default:
-        opp_error("Error processing command OPEN_PASSIVE: connection already exists");
-    }
+   IPvXAddress lAddr;
+   int16 localPort;
+
+   SCTPOpenCommand *openCmd = check_and_cast<SCTPOpenCommand *>(sctpCommand);
+
+   sctpEV3<<"SCTPAssociationEventProc:process_OPEN_PASSIVE\n";
+
+   switch(fsm->getState())
+   {
+      case SCTP_S_CLOSED:
+         initAssociation(openCmd);
+         state->fork = openCmd->getFork();
+         localAddressList = openCmd->getLocalAddresses();
+         sctpEV3<<"process_OPEN_PASSIVE: number of local addresses="<<localAddressList.size()<<"\n";
+         lAddr = openCmd->getLocalAddresses().front();
+         localPort = openCmd->getLocalPort();
+         state->localRwnd = (long)sctpMain->par("arwnd");
+         state->localMsgRwnd = sctpMain->par("messageAcceptLimit");
+         state->streamReset = openCmd->getStreamReset();
+         state->numRequests = openCmd->getNumRequests();
+         state->messagesToPush = openCmd->getMessagesToPush();
+
+         if (localPort==0)
+            opp_error("Error processing command OPEN_PASSIVE: local port must be specified");
+         sctpEV3 << "Assoc "<<assocId<<"::Starting to listen on: " << lAddr << ":" << localPort << "\n";
+
+         sctpMain->updateSockPair(this, lAddr, IPvXAddress(), localPort, 0);
+         break;
+      default:
+      opp_error("Error processing command OPEN_PASSIVE: connection already exists");
+   }
 }
 
 void SCTPAssociation::process_SEND(SCTPEventCode& event, SCTPCommand* sctpCommand, cPacket* msg)
 {
-    SCTPSendCommand* sendCommand = check_and_cast<SCTPSendCommand*>(sctpCommand);
-
-    if(fsm->getState() != SCTP_S_ESTABLISHED) {
-        // TD 12.03.2009: since SCTP_S_ESTABLISHED is the only case, the
-        // switch(...)-block has been removed for enhanced readability.
-        sctpEV3 << "process_SEND: state is not SCTP_S_ESTABLISHED -> returning" << endl;
-        return;
-    }
-
-    sctpEV3 << "process_SEND:"
-              << " assocId="      << assocId
-              << " localAddr="    << localAddr
-              << " remoteAddr="   << remoteAddr
-              << " cmdRemoteAddr="<< sendCommand->getRemoteAddr()
-              << " cmdPrimary="   << (sendCommand->getPrimary() ? "true" : "false")
-              << " appGateIndex=" << appGateIndex
-              << " streamId="     << sendCommand->getSid() << endl;
-
-    SCTPSimpleMessage* smsg = check_and_cast<SCTPSimpleMessage*>((msg->decapsulate()));
-    SCTP::AssocStatMap::iterator iter = sctpMain->assocStatMap.find(assocId);
-    iter->second.sentBytes += smsg->getBitLength() / 8;
+   SCTPSendCommand* sendCommand = check_and_cast<SCTPSendCommand*>(sctpCommand);
+
+   if(fsm->getState() != SCTP_S_ESTABLISHED) {
+      // TD 12.03.2009: since SCTP_S_ESTABLISHED is the only case, the
+      // switch(...)-block has been removed for enhanced readability.
+      sctpEV3 << "process_SEND: state is not SCTP_S_ESTABLISHED -> returning" << endl;
+      return;
+   }
+
+   sctpEV3 << "process_SEND:"
+           << " assocId="       << assocId
+           << " localAddr="     << localAddr
+           << " remoteAddr="    << remoteAddr
+           << " cmdRemoteAddr=" << sendCommand->getRemoteAddr()
+           << " cmdPrimary="    << (sendCommand->getPrimary() ? "true" : "false")
+           << " appGateIndex="  << appGateIndex
+           << " streamId="      << sendCommand->getSid() << endl;
+
+   SCTPSimpleMessage* smsg = check_and_cast<SCTPSimpleMessage*>((msg->decapsulate()));
+   SCTP::AssocStatMap::iterator iter = sctpMain->assocStatMap.find(assocId);
+   iter->second.sentBytes += smsg->getBitLength() / 8;
 
   // ------ Prepare SCTPDataMsg -----------------------------------------
-  const uint32 streamId       = sendCommand->getSid();
+  const uint32 streamId      = sendCommand->getSid();
   const uint32 sendUnordered = sendCommand->getSendUnordered();
-  const uint32 ppid           = sendCommand->getPpid();
+  const uint32 ppid          = sendCommand->getPpid();
   SCTPSendStream* stream = NULL;
   SCTPSendStreamMap::iterator associter = sendStreams.find(streamId);
   if (associter != sendStreams.end()) {
-     stream = associter->second;
+    stream = associter->second;
   }
   else {
-     opp_error("stream with id %d not found", streamId);
+    opp_error("stream with id %d not found", streamId);
   }
 
   char name[64];
@@ -154,132 +157,235 @@ void SCTPAssociation::process_SEND(SCTPEventCode& event, SCTPCommand* sctpComman
   datMsg->encapsulate(smsg);
   datMsg->setSid(streamId);
   datMsg->setPpid(ppid);
+  datMsg->setSackNow(sendCommand->getSackNow());
   datMsg->setEnqueuingTime(simulation.getSimTime());
 
+  // ------ PR-SCTP & Drop messages to free buffer space ----------------
+  datMsg->setPrMethod(sendCommand->getPrMethod());
+  switch (sendCommand->getPrMethod()) {
+    case PR_TTL:
+      if (sendCommand->getPrValue() > 0) {
+         datMsg->setExpiryTime(simulation.getSimTime() + sendCommand->getPrValue());
+      }
+      break;
+    case PR_RTX:
+      datMsg->setRtx((uint32)sendCommand->getPrValue());
+      break;
+    case PR_PRIO:
+      datMsg->setPriority((uint32)sendCommand->getPrValue());
+      state->queuedDroppableBytes += msg->getByteLength();
+      break;
+  }
+
+  if ((state->appSendAllowed)     &&
+      (state->sendQueueLimit > 0) &&
+	  (state->queuedDroppableBytes > 0) &&
+      ((uint64)state->sendBuffer >= state->sendQueueLimit) ) {
+   uint32 lowestPriority;
+   SCTPDataMsgQueue* strq;
+   int64 dropsize = state->sendBuffer - state->sendQueueLimit;
+   SCTPDataMsg* dropmsg;
+
+   if (sendUnordered)
+      strq = stream->getUnorderedStreamQ();
+   else
+      strq = stream->getStreamQ();
+
+    while (dropsize >= 0 && state->queuedDroppableBytes > 0) {
+      lowestPriority = 0;
+     dropmsg = NULL;
+
+      // Find lowest priority
+     for (cQueue::Iterator iter(*strq); !iter.end(); iter++) {
+        SCTPDataMsg* msg = (SCTPDataMsg*) iter();
+
+        if (msg->getPriority() > lowestPriority)
+          lowestPriority = msg->getPriority();
+      }
+
+      // If just passed message has the lowest priority,
+     // drop it and we're done.
+      if (datMsg->getPriority() > lowestPriority) {
+        sctpEV3 << "msg will be abandoned, buffer is full and priority too low ("
+                << datMsg->getPriority() << ")\n";
+        state->queuedDroppableBytes -= msg->getByteLength();
+       delete smsg;
+       delete msg;
+        sendIndicationToApp(SCTP_I_ABANDONED);
+        return;
+      }
+
+      // Find oldest message with lowest priority
+     for (cQueue::Iterator iter(*strq); !iter.end(); iter++) {
+        SCTPDataMsg* msg = (SCTPDataMsg*) iter();
+
+        if (msg->getPriority() == lowestPriority) {
+          if (!dropmsg ||
+             (dropmsg && dropmsg->getEnqueuingTime() < msg->getEnqueuingTime()))
+          lowestPriority = msg->getPriority();
+        }
+      }
+
+      if (dropmsg) {
+        strq->remove(dropmsg);
+        dropsize -= dropmsg->getByteLength();
+        state->queuedDroppableBytes -= dropmsg->getByteLength();
+        SCTPSimpleMessage* smsg = check_and_cast<SCTPSimpleMessage*>((msg->decapsulate()));
+        delete smsg;
+        delete dropmsg;
+        sendIndicationToApp(SCTP_I_ABANDONED);
+      }
+    }
+  }
+
   // ------ Set initial destination address -----------------------------
   if (sendCommand->getPrimary()) {
-     if (sendCommand->getRemoteAddr() == IPvXAddress("0.0.0.0")) {
-            datMsg->setInitialDestination(remoteAddr);
-     }
-     else {
-        datMsg->setInitialDestination(sendCommand->getRemoteAddr());
-     }
+    if (sendCommand->getRemoteAddr() == IPvXAddress("0.0.0.0")) {
+         datMsg->setInitialDestination(remoteAddr);
+    }
+    else {
+      datMsg->setInitialDestination(sendCommand->getRemoteAddr());
+    }
   }
   else {
-     datMsg->setInitialDestination(state->getPrimaryPathIndex());
+    datMsg->setInitialDestination(state->getPrimaryPathIndex());
   }
 
   // ------ Optional padding and size calculations ----------------------
+  if (state->padding) {
+     datMsg->setBooksize(ADD_PADDING(smsg->getBitLength() / 8 + state->header));
+  }
+  else {
      datMsg->setBooksize(smsg->getBitLength() / 8 + state->header);
-     qCounter.roomSumSendStreams += ADD_PADDING(smsg->getBitLength() / 8 + SCTP_DATA_CHUNK_LENGTH);
-     qCounter.bookedSumSendStreams += datMsg->getBooksize();
-     state->sendBuffer += smsg->getByteLength();
+  }
+
+  qCounter.roomSumSendStreams += ADD_PADDING(smsg->getBitLength() / 8 + SCTP_DATA_CHUNK_LENGTH);
+  qCounter.bookedSumSendStreams += datMsg->getBooksize();
+  // Add chunk size to sender buffer size
+  state->sendBuffer += smsg->getByteLength();
 
   datMsg->setMsgNum(++state->msgNum);
 
   // ------ Ordered/Unordered modes -------------------------------------
   if (sendUnordered == 1) {
-     datMsg->setOrdered(false);
-     stream->getUnorderedStreamQ()->insert(datMsg);
+    datMsg->setOrdered(false);
+    stream->getUnorderedStreamQ()->insert(datMsg);
   }
   else {
-     datMsg->setOrdered(true);
-     stream->getStreamQ()->insert(datMsg);
-
-     if ((state->appSendAllowed)        &&
-          (state->sendQueueLimit > 0) &&
-          ((uint64)state->sendBuffer >= state->sendQueueLimit) ) {
-        sendIndicationToApp(SCTP_I_SENDQUEUE_FULL);
-        state->appSendAllowed = false;
-     }
-     sendQueue->record(stream->getStreamQ()->getLength());
+    datMsg->setOrdered(true);
+    stream->getStreamQ()->insert(datMsg);
+
+    sendQueue->record(stream->getStreamQ()->getLength());
+  }
+
+  // ------ Send buffer full? -------------------------------------------
+  if ((state->appSendAllowed)     &&
+      (state->sendQueueLimit > 0) &&
+      ((uint64)state->sendBuffer >= state->sendQueueLimit) ) {
+   // If there are not enough messages that could be dropped,
+   // the buffer is really full and the app has to be notified.
+   if (state->queuedDroppableBytes < state->sendBuffer - state->sendQueueLimit) {
+      sendIndicationToApp(SCTP_I_SENDQUEUE_FULL);
+      state->appSendAllowed = false;
+    }
   }
 
   state->queuedMessages++;
   if ((state->queueLimit > 0) && (state->queuedMessages > state->queueLimit)) {
-     state->queueUpdate = false;
+    state->queueUpdate = false;
   }
   sctpEV3 << "process_SEND:"
-             << " last="         << sendCommand->getLast()
-             <<"    queueLimit=" << state->queueLimit << endl;
+          << " last="       << sendCommand->getLast()
+          <<"  queueLimit=" << state->queueLimit << endl;
 
   // ------ Call sendCommandInvoked() to send message -------------------
   // sendCommandInvoked() itself will call sendOnAllPaths() ...
   if (sendCommand->getLast() == true) {
-     if (sendCommand->getPrimary()) {
-        sctpAlgorithm->sendCommandInvoked(NULL);
-     }
-     else {
-        sctpAlgorithm->sendCommandInvoked(getPath(datMsg->getInitialDestination()));
-     }
+    if (sendCommand->getPrimary()) {
+      sctpAlgorithm->sendCommandInvoked(NULL);
+    }
+    else {
+      sctpAlgorithm->sendCommandInvoked(getPath(datMsg->getInitialDestination()));
+    }
   }
 }
 
 void SCTPAssociation::process_RECEIVE_REQUEST(SCTPEventCode& event, SCTPCommand *sctpCommand)
 {
-     SCTPSendCommand *sendCommand = check_and_cast<SCTPSendCommand *>(sctpCommand);
-    if ((uint32)sendCommand->getSid() > inboundStreams || sendCommand->getSid() < 0)
-    {
-        sctpEV3<<"Application tries to read from invalid stream id....\n";
-    }
-    state->numMsgsReq[sendCommand->getSid()]+= sendCommand->getNumMsgs();
-    pushUlp();
+    SCTPSendCommand *sendCommand = check_and_cast<SCTPSendCommand *>(sctpCommand);
+   if ((uint32)sendCommand->getSid() > inboundStreams || sendCommand->getSid() < 0)
+   {
+      sctpEV3<<"Application tries to read from invalid stream id....\n";
+   }
+   state->numMsgsReq[sendCommand->getSid()]+= sendCommand->getNumMsgs();
+   pushUlp();
 }
 
 void SCTPAssociation::process_PRIMARY(SCTPEventCode& event, SCTPCommand *sctpCommand)
 {
-    SCTPPathInfo *pinfo = check_and_cast<SCTPPathInfo *>(sctpCommand);
-    state->setPrimaryPath(getPath(pinfo->getRemoteAddress()));
+   SCTPPathInfo *pinfo = check_and_cast<SCTPPathInfo *>(sctpCommand);
+   state->setPrimaryPath(getPath(pinfo->getRemoteAddress()));
 }
 
+void SCTPAssociation::process_STREAM_RESET(SCTPCommand *sctpCommand)
+{
+   sctpEV3<<"process_STREAM_RESET request arriving from App\n";
+   SCTPResetInfo *rinfo = check_and_cast<SCTPResetInfo *>(sctpCommand);
+   if (!(getPath(remoteAddr)->ResetTimer->isScheduled()))
+   {
+      sendStreamResetRequest(rinfo->getRequestType());
+      if (rinfo->getRequestType()==RESET_OUTGOING || rinfo->getRequestType()==RESET_BOTH || rinfo->getRequestType()==SSN_TSN)
+         state->resetPending = true;
+   }
+}
 
 void SCTPAssociation::process_QUEUE_MSGS_LIMIT(const SCTPCommand* sctpCommand)
 {
-    const SCTPInfo* qinfo = check_and_cast<const SCTPInfo*>(sctpCommand);
-    state->queueLimit = qinfo->getText();
-    sctpEV3<<"state->queueLimit set to "<<state->queueLimit<<"\n";
+   const SCTPInfo* qinfo = check_and_cast<const SCTPInfo*>(sctpCommand);
+   state->queueLimit = qinfo->getText();
+   sctpEV3<<"state->queueLimit set to "<<state->queueLimit<<"\n";
 }
 
 void SCTPAssociation::process_QUEUE_BYTES_LIMIT(const SCTPCommand* sctpCommand)
 {
-    const SCTPInfo* qinfo = check_and_cast<const SCTPInfo*>(sctpCommand);
-    state->sendQueueLimit = qinfo->getText();
+   const SCTPInfo* qinfo = check_and_cast<const SCTPInfo*>(sctpCommand);
+   state->sendQueueLimit = qinfo->getText();
 }
 
 void SCTPAssociation::process_CLOSE(SCTPEventCode& event)
 {
-    sctpEV3 << "SCTPAssociationEventProc:process_CLOSE; assoc=" << assocId << endl;
-    switch(fsm->getState()) {
-        case SCTP_S_ESTABLISHED:
-            sendOnAllPaths(state->getPrimaryPath());
-            sendShutdown();
-            break;
-        case SCTP_S_SHUTDOWN_RECEIVED:
-            if (getOutstandingBytes() == 0) {
-                sendShutdownAck(remoteAddr);
-            }
-            break;
-     }
+   sctpEV3 << "SCTPAssociationEventProc:process_CLOSE; assoc=" << assocId << endl;
+   switch(fsm->getState()) {
+      case SCTP_S_ESTABLISHED:
+         sendOnAllPaths(state->getPrimaryPath());
+         sendShutdown();
+         break;
+      case SCTP_S_SHUTDOWN_RECEIVED:
+         if (getOutstandingBytes() == 0) {
+            sendShutdownAck(remoteAddr);
+         }
+         break;
+    }
 }
 
 void SCTPAssociation::process_ABORT(SCTPEventCode& event)
 {
-    sctpEV3 << "SCTPAssociationEventProc:process_ABORT; assoc=" << assocId << endl;
-    switch(fsm->getState()) {
-        case SCTP_S_ESTABLISHED:
-            sendOnAllPaths(state->getPrimaryPath());
-            sendAbort();
-            break;
-    }
+   sctpEV3 << "SCTPAssociationEventProc:process_ABORT; assoc=" << assocId << endl;
+   switch(fsm->getState()) {
+      case SCTP_S_ESTABLISHED:
+         sendOnAllPaths(state->getPrimaryPath());
+         sendAbort();
+         break;
+   }
 }
 
 void SCTPAssociation::process_STATUS(SCTPEventCode& event, SCTPCommand *sctpCommand, cPacket *msg)
 {
-    SCTPStatusInfo *statusInfo = new SCTPStatusInfo();
-    statusInfo->setState(fsm->getState());
-    statusInfo->setStateName(stateName(fsm->getState()));
-    statusInfo->setPathId(remoteAddr);
-    statusInfo->setActive(getPath(remoteAddr)->activePath);
-    msg->setControlInfo(statusInfo);
-    sendToApp(msg);
+   SCTPStatusInfo *statusInfo = new SCTPStatusInfo();
+   statusInfo->setState(fsm->getState());
+   statusInfo->setStateName(stateName(fsm->getState()));
+   statusInfo->setPathId(remoteAddr);
+   statusInfo->setActive(getPath(remoteAddr)->activePath);
+   msg->setControlInfo(statusInfo);
+   sendToApp(msg);
 }
diff --git a/src/transport/sctp/SCTPAssociationRcvMessage.cc b/src/transport/sctp/SCTPAssociationRcvMessage.cc
index fd11fa4..49b2363 100644
--- a/src/transport/sctp/SCTPAssociationRcvMessage.cc
+++ b/src/transport/sctp/SCTPAssociationRcvMessage.cc
@@ -1,6 +1,6 @@
 //
-// Copyright (C) 2005-2010 Irene Ruengeler
-// Copyright (C) 2009-2010 Thomas Dreibholz
+// Copyright (C) 2008-2009 Irene Ruengeler
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -28,679 +28,886 @@
 #include "IPv6InterfaceData.h"
 #include "IPControlInfo_m.h"
 
+// ====== Activate debugging mode here! ===================
+// #define RCVMESSAGE_DEBUG
+// ========================================================
+
 
 void SCTPAssociation::decreaseOutstandingBytes(SCTPDataVariables* chunk)
 {
-    SCTPPathVariables* lastPath = chunk->getLastDestinationPath();
-
-    assert(lastPath->outstandingBytes >= chunk->booksize);
-    lastPath->outstandingBytes -= chunk->booksize;
-    state->outstandingBytes -= chunk->booksize;
-    assert((int64)state->outstandingBytes >= 0);
-
-    chunk->countsAsOutstanding = false;
-
-    CounterMap::iterator iterator = qCounter.roomRetransQ.find(lastPath->remoteAddress);
-        iterator->second -= ADD_PADDING(chunk->booksize + SCTP_DATA_CHUNK_LENGTH);
-
+   SCTPPathVariables* lastPath = chunk->getLastDestinationPath();
+   if(SCTP::checkQueues) {
+      sctpEV3 << "Paths before decreaseOutstandingBytes(): ";
+      dumpPaths();
+      sctpEV3 << "Decreasing osb on path " << chunk->getLastDestination()
+            << " from " << lastPath->outstandingBytes << " by "
+            << chunk->booksize << " to "
+            << lastPath->outstandingBytes - chunk->booksize
+            << " [chunk TSN=" << chunk->tsn << ", SSN=" << chunk->ssn << "]"
+            << " ..." << endl;
+   }
+
+   if(chunk->countsAsOutstanding) {
+      assert(lastPath->outstandingBytes >= chunk->booksize);
+      lastPath->outstandingBytes -= chunk->booksize;
+      lastPath->statisticsPathOutstandingBytes->record(lastPath->outstandingBytes);
+      state->outstandingBytes -= chunk->booksize;
+      assert((int64)state->outstandingBytes >= 0);
+      statisticsOutstandingBytes->record(state->outstandingBytes);
+
+      CounterMap::iterator iterator = qCounter.roomRetransQ.find(lastPath->remoteAddress);
+      state->outstandingMessages--;
+      if (state->osbWithHeader)
+         iterator->second -= ADD_PADDING(chunk->booksize);
+      else
+         iterator->second -= ADD_PADDING(chunk->booksize + SCTP_DATA_CHUNK_LENGTH);
+
+      chunk->countsAsOutstanding = false;
+   }
+
+   if(SCTP::checkQueues) {
+      sctpEV3 << "Paths after decreaseOutstandingBytes(): ";
+      dumpPaths();
+      checkOutstandingBytes();
+   }
 }
 
 
 bool SCTPAssociation::process_RCV_Message(SCTPMessage*       sctpmsg,
-                                                        const IPvXAddress& src,
-                                                        const IPvXAddress& dest)
+                                          const IPvXAddress& src,
+                                          const IPvXAddress& dest)
 {
-    // ====== Header checks ==================================================
-    sctpEV3 << getFullPath()  << " SCTPAssociationRcvMessage:process_RCV_Message"
-              << " localAddr="  << localAddr
-              << " remoteAddr=" << remoteAddr << endl;
-    if ((sctpmsg->hasBitError() || !sctpmsg->getChecksumOk()))
-    {
-        if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType() == INIT_ACK) {
-            stopTimer(T1_InitTimer);
-            sctpEV3 << "InitAck with bit-error. Retransmit Init" << endl;
-            retransmitInit();
-            startTimer(T1_InitTimer,state->initRexmitTimeout);
-        }
-        if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType() == COOKIE_ACK) {
-            stopTimer(T1_InitTimer);
-            sctpEV3 << "CookieAck with bit-error. Retransmit CookieEcho" << endl;
-            retransmitCookieEcho();
-            startTimer(T1_InitTimer,state->initRexmitTimeout);
-        }
-    }
-
-    SCTPPathVariables* path      = getPath(src);
-    const uint16 srcPort         = sctpmsg->getDestPort();
-    const uint16 destPort        = sctpmsg->getSrcPort();
-    const uint32 numberOfChunks = sctpmsg->getChunksArraySize();
-    sctpEV3 << "numberOfChunks=" <<numberOfChunks << endl;
-
-    state->sctpmsg = (SCTPMessage*)sctpmsg->dup();
-
-    // ====== Handle chunks ==================================================
-    bool trans              = true;
-    bool sendAllowed        = false;
-    bool dupReceived        = false;
-    bool dataChunkReceived  = false;
-    bool dataChunkDelivered = false;
-    bool shutdownCalled     = false;
-    for (uint32 i = 0; i < numberOfChunks; i++) {
-        const SCTPChunk* header = (const SCTPChunk*)(sctpmsg->removeChunk());
-        const uint8       type  = header->getChunkType();
-
-        if ((type != INIT) &&
-             (type != ABORT) &&
-             (type != ERRORTYPE) &&
-             (sctpmsg->getTag() != peerVTag)) {
-            sctpEV3 << " VTag "<< sctpmsg->getTag() << " incorrect. Should be "
-                      << peerVTag << " localVTag=" << localVTag << endl;
-            return true;
-        }
-
-        switch (type) {
-        case INIT:
-            sctpEV3 << "SCTPAssociationRcvMessage: INIT received" << endl;
-            SCTPInitChunk* initChunk;
-            initChunk = check_and_cast<SCTPInitChunk*>(header);
-            if ((initChunk->getNoInStreams() != 0) &&
-                 (initChunk->getNoOutStreams() != 0) &&
-                 (initChunk->getInitTag() != 0)) {
-                trans = processInitArrived(initChunk, srcPort, destPort);
-            }
-            i = numberOfChunks-1;
-            delete initChunk;
-            break;
-        case INIT_ACK:
-            sctpEV3 << "SCTPAssociationRcvMessage: INIT_ACK received" << endl;
-            if (fsm->getState() == SCTP_S_COOKIE_WAIT) {
-                SCTPInitAckChunk* initAckChunk;
-                initAckChunk = check_and_cast<SCTPInitAckChunk*>(header);
-                if ((initAckChunk->getNoInStreams() != 0) &&
-                    (initAckChunk->getNoOutStreams() != 0) &&
-                    (initAckChunk->getInitTag() != 0)) {
-                    trans = processInitAckArrived(initAckChunk);
-                }
-                else if (initAckChunk->getInitTag() == 0) {
-                    sendAbort();
-                    sctpMain->removeAssociation(this);
-                }
-                i = numberOfChunks-1;
-                delete initAckChunk;
-            }
-            else {
-                sctpEV3 << "INIT_ACK will be ignored" << endl;
-            }
-            break;
-        case COOKIE_ECHO:
-            sctpEV3 << "SCTPAssociationRcvMessage: COOKIE_ECHO received" << endl;
-            SCTPCookieEchoChunk* cookieEchoChunk;
-            cookieEchoChunk = check_and_cast<SCTPCookieEchoChunk*>(header);
-            trans = processCookieEchoArrived(cookieEchoChunk,src);
-            delete cookieEchoChunk;
-            break;
-        case COOKIE_ACK:
-            sctpEV3 << "SCTPAssociationRcvMessage: COOKIE_ACK received" << endl;
-            if (fsm->getState() == SCTP_S_COOKIE_ECHOED) {
-                SCTPCookieAckChunk* cookieAckChunk;
-                cookieAckChunk = check_and_cast<SCTPCookieAckChunk*>(header);
-                trans = processCookieAckArrived();
-                delete cookieAckChunk;
-            }
-            break;
-        case DATA:
-            sctpEV3 << "SCTPAssociationRcvMessage: DATA received" << endl;
-            if (fsm->getState() == SCTP_S_COOKIE_ECHOED) {
-                trans = performStateTransition(SCTP_E_RCV_COOKIE_ACK);
-            }
-            if (!(fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED || fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)) {
-                SCTPDataChunk* dataChunk;
-                dataChunk = check_and_cast<SCTPDataChunk*>(header);
-                if ((dataChunk->getByteLength()- 16) > 0) {
-                    const SCTPEventCode event = processDataArrived(dataChunk);
-                    if (event == SCTP_E_DELIVERED) {
-                        dataChunkReceived    = true;
-                        dataChunkDelivered = true;
-                        state->sackAllowed = true;
-                    }
-                    else if (event==SCTP_E_SEND || event==SCTP_E_IGNORE) {
-                        dataChunkReceived    = true;
-                        state->sackAllowed = true;
-                    }
-                    else if (event==SCTP_E_DUP_RECEIVED) {
-                        dupReceived = true;
-                    }
-                }
-                else {
-                    sendAbort();
-                    sctpMain->removeAssociation(this);
-                }
-                delete dataChunk;
-            }
-            trans = true;
-            break;
-        case SACK:
-        {
-            sctpEV3 << "SCTPAssociationRcvMessage: SACK received" << endl;
-            const int32 scount = qCounter.roomSumSendStreams;
-            SCTPSackChunk* sackChunk;
-            sackChunk = check_and_cast<SCTPSackChunk*>(header);
-            processSackArrived(sackChunk);
-            trans           = true;
-            sendAllowed = true;
-            delete sackChunk;
-            if (getOutstandingBytes()==0 && transmissionQ->getQueueSize()==0 && scount==0) {
-                if (fsm->getState() == SCTP_S_SHUTDOWN_PENDING) {
-                    sctpEV3 << "No more packets: send SHUTDOWN" << endl;
-                    sendShutdown();
-                    trans               = performStateTransition(SCTP_E_NO_MORE_OUTSTANDING);
-                    shutdownCalled = true;
-                }
-                else if (fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED) {
-                    sctpEV3 << "No more outstanding" << endl;
-                    sendShutdownAck(remoteAddr);
-                }
+   // ====== Header checks ==================================================
+   sctpEV3 << getFullPath()  << " SCTPAssociationRcvMessage:process_RCV_Message"
+           << " localAddr="  << localAddr
+           << " remoteAddr=" << remoteAddr << endl;
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+   state->pktDropSent = false;
+   if ((sctpmsg->hasBitError() || !sctpmsg->getChecksumOk()))
+   {
+      if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType() == INIT_ACK) {
+         stopTimer(T1_InitTimer);
+         sctpEV3 << "InitAck with bit-error. Retransmit Init" << endl;
+         retransmitInit();
+         startTimer(T1_InitTimer,state->initRexmitTimeout);
+      }
+      if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType() == COOKIE_ACK) {
+         stopTimer(T1_InitTimer);
+         sctpEV3 << "CookieAck with bit-error. Retransmit CookieEcho" << endl;
+         retransmitCookieEcho();
+         startTimer(T1_InitTimer,state->initRexmitTimeout);
+      }
+      if (!(sctpMain->pktdrop) || !state->peerPktDrop) {
+         sctpEV3<<"Packet has bit-error. Return\n";
+         return true;
+      }
+   }
+
+   SCTPPathVariables* path     = getPath(src);
+   const int32  srcPort        = sctpmsg->getDestPort();
+   const int32  destPort       = sctpmsg->getSrcPort();
+   const uint32 numberOfChunks = sctpmsg->getChunksArraySize();
+   sctpEV3 << "numberOfChunks=" <<numberOfChunks << endl;
+
+   state->sctpmsg = (SCTPMessage*)sctpmsg->dup();
+   bool authenticationNecessary = state->peerAuth;
+   if ((sctpmsg->getChecksumOk() == false || sctpmsg->hasBitError()) &&
+       (sctpMain->pktdrop) &&
+       (state->peerPktDrop)) {
+      sendPacketDrop(true);
+      return true;
+   }
+
+
+   if (fsm->getState()!=SCTP_S_CLOSED &&
+       fsm->getState()!=SCTP_S_COOKIE_WAIT &&
+       fsm->getState()!=SCTP_S_COOKIE_ECHOED &&
+       fsm->getState()!=SCTP_S_SHUTDOWN_ACK_SENT &&
+       simTime() > state->lastAssocThroughputTime + state->throughputInterval &&
+       assocThroughputVector != NULL) {
+      assocThroughputVector->record(state->assocThroughput / (simTime() - state->lastAssocThroughputTime) / 1000);
+      state->lastAssocThroughputTime = simTime();
+      state->assocThroughput = 0;
+   }
+   state->assocThroughput += sctpmsg->getByteLength();
+
+   // ====== Handle chunks ==================================================
+   bool trans             = true;
+   bool sendAllowed       = false;
+   bool dupReceived       = false;
+   bool dataChunkReceived = false;
+   bool shutdownCalled    = false;
+   for (uint32 i = 0; i < numberOfChunks; i++) {
+      const SCTPChunk* header = (const SCTPChunk*)(sctpmsg->removeChunk());
+      const uint8      type   = header->getChunkType();
+
+      if ((type != INIT) &&
+          (type != ABORT) &&
+          (type != ERRORTYPE) &&
+          (sctpmsg->getTag() != peerVTag)) {
+         sctpEV3 << " VTag "<< sctpmsg->getTag() << " incorrect. Should be "
+                 << peerVTag << " localVTag=" << localVTag << endl;
+         return true;
+      }
+
+      if (authenticationNecessary) {
+         if (type == AUTH) {
+            sctpEV3 << "SCTPAssociationRcvMessage: AUTH received" << endl;
+            SCTPAuthenticationChunk* authChunk;
+            authChunk = check_and_cast<SCTPAuthenticationChunk*>(header);
+            if (authChunk->getHMacIdentifier() != 1) {
+               sendHMacError(authChunk->getHMacIdentifier());
+               return true;
             }
-            break;
-        }
-        case ABORT:
-            SCTPAbortChunk* abortChunk;
-            abortChunk = check_and_cast<SCTPAbortChunk*>(header);
-            sctpEV3 << "SCTPAssociationRcvMessage: ABORT with T-Bit "
-                      << abortChunk->getT_Bit() << " received" << endl;
-            if (sctpmsg->getTag() == localVTag || sctpmsg->getTag() == peerVTag) {
-                sendIndicationToApp(SCTP_I_ABORT);
-                trans = performStateTransition(SCTP_E_ABORT);
+            if (authChunk->getHMacOk() == false) {
+               delete authChunk;
+               return true;
             }
-            delete abortChunk;
-            break;
-        case HEARTBEAT:
-            sctpEV3 << "SCTPAssociationRcvMessage: HEARTBEAT received" << endl;
-            SCTPHeartbeatChunk* heartbeatChunk;
-            heartbeatChunk = check_and_cast<SCTPHeartbeatChunk*>(header);
-            if (!(fsm->getState() == SCTP_S_CLOSED)) {
-                sendHeartbeatAck(heartbeatChunk, dest, src);
+            authenticationNecessary = false;
+            delete authChunk;
+            continue;
+         }
+         else
+         {
+            if (typeInChunkList(type)) {
+               return true;
             }
-            trans = true;
-            delete heartbeatChunk;
-            if (path) {
-                path->numberOfHeartbeatsRcvd++;
-                path->pathRcvdHb->record(path->numberOfHeartbeatsRcvd);
+         }
+      }
+
+      switch (type) {
+      case INIT:
+         sctpEV3 << "SCTPAssociationRcvMessage: INIT received" << endl;
+         SCTPInitChunk* initChunk;
+         initChunk = check_and_cast<SCTPInitChunk*>(header);
+         if ((initChunk->getNoInStreams() != 0) &&
+             (initChunk->getNoOutStreams() != 0) &&
+             (initChunk->getInitTag() != 0)) {
+            trans = processInitArrived(initChunk, srcPort, destPort);
+         }
+         i = numberOfChunks-1;
+         delete initChunk;
+         break;
+      case INIT_ACK:
+         sctpEV3 << "SCTPAssociationRcvMessage: INIT_ACK received" << endl;
+         if (fsm->getState() == SCTP_S_COOKIE_WAIT) {
+            SCTPInitAckChunk* initAckChunk;
+            initAckChunk = check_and_cast<SCTPInitAckChunk*>(header);
+            if ((initAckChunk->getNoInStreams() != 0) &&
+               (initAckChunk->getNoOutStreams() != 0) &&
+               (initAckChunk->getInitTag() != 0)) {
+               trans = processInitAckArrived(initAckChunk);
             }
-            break;
-        case HEARTBEAT_ACK:
-            sctpEV3 << "SCTPAssociationRcvMessage: HEARTBEAT_ACK received" << endl;
-            if (fsm->getState() == SCTP_S_COOKIE_ECHOED) {
-                trans = performStateTransition(SCTP_E_RCV_COOKIE_ACK);
+            else if (initAckChunk->getInitTag() == 0) {
+               sendAbort();
+               sctpMain->removeAssociation(this);
             }
-            SCTPHeartbeatAckChunk* heartbeatAckChunk;
-            heartbeatAckChunk = check_and_cast<SCTPHeartbeatAckChunk*>(header);
-            if (path) {
-                processHeartbeatAckArrived(heartbeatAckChunk, path);
+            i = numberOfChunks-1;
+            delete initAckChunk;
+         }
+         else {
+            sctpEV3 << "INIT_ACK will be ignored" << endl;
+         }
+         break;
+      case COOKIE_ECHO:
+         sctpEV3 << "SCTPAssociationRcvMessage: COOKIE_ECHO received" << endl;
+         SCTPCookieEchoChunk* cookieEchoChunk;
+         cookieEchoChunk = check_and_cast<SCTPCookieEchoChunk*>(header);
+         trans = processCookieEchoArrived(cookieEchoChunk,src);
+         delete cookieEchoChunk;
+         break;
+      case COOKIE_ACK:
+         sctpEV3 << "SCTPAssociationRcvMessage: COOKIE_ACK received" << endl;
+         if (fsm->getState() == SCTP_S_COOKIE_ECHOED) {
+            SCTPCookieAckChunk* cookieAckChunk;
+            cookieAckChunk = check_and_cast<SCTPCookieAckChunk*>(header);
+            trans = processCookieAckArrived();
+            delete cookieAckChunk;
+         }
+         break;
+      case DATA:
+         sctpEV3 << "SCTPAssociationRcvMessage: DATA received" << endl;
+         if (fsm->getState() == SCTP_S_COOKIE_ECHOED) {
+            trans = performStateTransition(SCTP_E_RCV_COOKIE_ACK);
+         }
+         if (!(fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED || fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)) {
+            SCTPDataChunk* dataChunk;
+            dataChunk = check_and_cast<SCTPDataChunk*>(header);
+            if ((dataChunk->getByteLength()- 16) > 0) {
+               const SCTPEventCode event = processDataArrived(dataChunk);
+               if (event == SCTP_E_DELIVERED) {
+                  if ((state->streamReset) &&
+                      (state->lastTsnBeforeReset == state->gapList.getHighestTSNReceived())) {
+                     resetExpectedSsns();
+                     if (state->inOut) {
+                        sendOutgoingRequestAndResponse(state->inRequestSn, state->peerRequestSn);
+                     }
+                     else {
+                        sendStreamResetResponse(state->peerRequestSn);
+                     }
+                  }
+                  dataChunkReceived  = true;
+                  state->sackAllowed = true;
+               }
+               else if (event==SCTP_E_SEND || event==SCTP_E_IGNORE) {
+                  dataChunkReceived  = true;
+                  state->sackAllowed = true;
+               }
+               else if (event==SCTP_E_DUP_RECEIVED) {
+                  dupReceived = true;
+               }
             }
-            trans = true;
-            delete heartbeatAckChunk;
-            break;
-        case SHUTDOWN:
-            sctpEV3 << "SCTPAssociationRcvMessage: SHUTDOWN received" << endl;
-            SCTPShutdownChunk* shutdownChunk;
-            shutdownChunk = check_and_cast<SCTPShutdownChunk*>(header);
-            if (shutdownChunk->getCumTsnAck()>state->lastTsnAck) {
-                simtime_t rttEstimation = MAXTIME;
-                dequeueAckedChunks(shutdownChunk->getCumTsnAck(),
-                                         getPath(remoteAddr), rttEstimation);
-                state->lastTsnAck = shutdownChunk->getCumTsnAck();
+            else {
+               sendAbort();
+               sctpMain->removeAssociation(this);
             }
-            trans = performStateTransition(SCTP_E_RCV_SHUTDOWN);
-            sendIndicationToApp(SCTP_I_SHUTDOWN_RECEIVED);
-            trans = true;
-            delete shutdownChunk;
-            break;
-        case SHUTDOWN_ACK:
-            sctpEV3 << "SCTPAssociationRcvMessage: SHUTDOWN_ACK received" << endl;
-            if (fsm->getState()!=SCTP_S_ESTABLISHED) {
-                SCTPShutdownAckChunk* shutdownAckChunk;
-                shutdownAckChunk = check_and_cast<SCTPShutdownAckChunk*>(header);
-                sendShutdownComplete();
-                stopTimers();
-                stopTimer(T2_ShutdownTimer);
-                stopTimer(T5_ShutdownGuardTimer);
-                sctpEV3 << "state=" << stateName(fsm->getState()) << endl;
-                if ((fsm->getState() == SCTP_S_SHUTDOWN_SENT) ||
-                     (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)) {
-                    trans = performStateTransition(SCTP_E_RCV_SHUTDOWN_ACK);
-                    sendIndicationToApp(SCTP_I_CLOSED);
-                    delete state->shutdownChunk;
-                }
-                delete shutdownAckChunk;
+            delete dataChunk;
+         }
+         trans = true;
+         break;
+      case SACK:
+      {
+         sctpEV3 << "SCTPAssociationRcvMessage: SACK received" << endl;
+         const int32 scount = qCounter.roomSumSendStreams;
+         SCTPSackChunk* sackChunk;
+         sackChunk = check_and_cast<SCTPSackChunk*>(header);
+         processSackArrived(sackChunk);
+         trans       = true;
+         sendAllowed = true;
+         delete sackChunk;
+         if (getOutstandingBytes()==0 && transmissionQ->getQueueSize()==0 && scount==0) {
+            if (fsm->getState() == SCTP_S_SHUTDOWN_PENDING) {
+               sctpEV3 << "No more packets: send SHUTDOWN" << endl;
+               sendShutdown();
+               trans          = performStateTransition(SCTP_E_NO_MORE_OUTSTANDING);
+               shutdownCalled = true;
             }
-            break;
-        case SHUTDOWN_COMPLETE:
-            sctpEV3<<"Shutdown Complete arrived" << endl;
-            SCTPShutdownCompleteChunk* shutdownCompleteChunk;
-            shutdownCompleteChunk = check_and_cast<SCTPShutdownCompleteChunk*>(header);
-            trans = performStateTransition(SCTP_E_RCV_SHUTDOWN_COMPLETE);
-            sendIndicationToApp(SCTP_I_PEER_CLOSED);     // necessary for NAT-Rendezvous
-            if (trans == true) {
-                stopTimers();
+            else if (fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED) {
+               sctpEV3 << "No more outstanding" << endl;
+               sendShutdownAck(remoteAddr);
             }
+         }
+         break;
+      }
+      case ABORT:
+         SCTPAbortChunk* abortChunk;
+         abortChunk = check_and_cast<SCTPAbortChunk*>(header);
+         sctpEV3 << "SCTPAssociationRcvMessage: ABORT with T-Bit "
+                 << abortChunk->getT_Bit() << " received" << endl;
+         if (sctpmsg->getTag() == localVTag || sctpmsg->getTag() == peerVTag) {
+            sendIndicationToApp(SCTP_I_ABORT);
+            trans = performStateTransition(SCTP_E_ABORT);
+         }
+         delete abortChunk;
+         break;
+      case HEARTBEAT:
+         sctpEV3 << "SCTPAssociationRcvMessage: HEARTBEAT received" << endl;
+         SCTPHeartbeatChunk* heartbeatChunk;
+         heartbeatChunk = check_and_cast<SCTPHeartbeatChunk*>(header);
+         if (!(fsm->getState() == SCTP_S_CLOSED)) {
+            sendHeartbeatAck(heartbeatChunk, dest, src);
+         }
+         trans = true;
+         delete heartbeatChunk;
+         break;
+      case HEARTBEAT_ACK:
+         sctpEV3 << "SCTPAssociationRcvMessage: HEARTBEAT_ACK received" << endl;
+         if (fsm->getState() == SCTP_S_COOKIE_ECHOED) {
+            trans = performStateTransition(SCTP_E_RCV_COOKIE_ACK);
+         }
+         SCTPHeartbeatAckChunk* heartbeatAckChunk;
+         heartbeatAckChunk = check_and_cast<SCTPHeartbeatAckChunk*>(header);
+         if (path) {
+            processHeartbeatAckArrived(heartbeatAckChunk, path);
+         }
+         trans = true;
+         delete heartbeatAckChunk;
+         break;
+      case SHUTDOWN:
+         sctpEV3 << "SCTPAssociationRcvMessage: SHUTDOWN received" << endl;
+         SCTPShutdownChunk* shutdownChunk;
+         shutdownChunk = check_and_cast<SCTPShutdownChunk*>(header);
+         if (shutdownChunk->getCumTsnAck()>state->lastTsnAck) {
+            simtime_t rttEstimation = MAXTIME;
+            dequeueAckedChunks(shutdownChunk->getCumTsnAck(),
+                               getPath(remoteAddr), rttEstimation);
+            state->lastTsnAck = shutdownChunk->getCumTsnAck();
+         }
+         trans = performStateTransition(SCTP_E_RCV_SHUTDOWN);
+         sendIndicationToApp(SCTP_I_SHUTDOWN_RECEIVED);
+         trans = true;
+         delete shutdownChunk;
+         break;
+      case SHUTDOWN_ACK:
+         sctpEV3 << "SCTPAssociationRcvMessage: SHUTDOWN_ACK received" << endl;
+         if (fsm->getState()!=SCTP_S_ESTABLISHED) {
+            SCTPShutdownAckChunk* shutdownAckChunk;
+            shutdownAckChunk = check_and_cast<SCTPShutdownAckChunk*>(header);
+            sendShutdownComplete();
+            stopTimers();
             stopTimer(T2_ShutdownTimer);
             stopTimer(T5_ShutdownGuardTimer);
-            delete state->shutdownAckChunk;
-            delete shutdownCompleteChunk;
-            break;
-        default: sctpEV3<<"different type" << endl;
-            break;
-        }
-
-        if (i==numberOfChunks-1 && (dataChunkReceived || dupReceived)) {
-            sendAllowed=true;
-            sctpEV3 << "i=" << i << " sendAllowed=true; scheduleSack" << endl;
-            scheduleSack();
-            if (fsm->getState() == SCTP_S_SHUTDOWN_SENT && state->ackState>=sackFrequency) {
-                sendSack();
+            sctpEV3 << "state=" << stateName(fsm->getState()) << endl;
+            if ((fsm->getState() == SCTP_S_SHUTDOWN_SENT) ||
+                (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)) {
+               trans = performStateTransition(SCTP_E_RCV_SHUTDOWN_ACK);
+               sendIndicationToApp(SCTP_I_CLOSED);
+               delete state->shutdownChunk;
             }
-        }
-
-        // Send any new DATA chunks, SACK chunks, HEARTBEAT chunks etc.
-        sctpEV3 << "SCTPAssociationRcvMessage: send new data? state=" << stateName(fsm->getState())
-                  << " sendAllowed="        << sendAllowed
-                  << " shutdownCalled=" << shutdownCalled << endl;
-        if (((fsm->getState() == SCTP_S_ESTABLISHED) ||
-              (fsm->getState() == SCTP_S_SHUTDOWN_PENDING) ||
-              (fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED)) &&
-             (sendAllowed) &&
-             (!shutdownCalled)) {
-            sendOnAllPaths(state->getPrimaryPath());
-        }
-    }
-
-    // ====== Clean-up =======================================================
-    disposeOf(state->sctpmsg);
-    return trans;
+            delete shutdownAckChunk;
+         }
+         break;
+      case SHUTDOWN_COMPLETE:
+         sctpEV3<<"Shutdown Complete arrived" << endl;
+         SCTPShutdownCompleteChunk* shutdownCompleteChunk;
+         shutdownCompleteChunk = check_and_cast<SCTPShutdownCompleteChunk*>(header);
+         trans = performStateTransition(SCTP_E_RCV_SHUTDOWN_COMPLETE);
+         sendIndicationToApp(SCTP_I_PEER_CLOSED);   // necessary for NAT-Rendezvous
+         if (trans == true) {
+            stopTimers();
+         }
+         stopTimer(T2_ShutdownTimer);
+         stopTimer(T5_ShutdownGuardTimer);
+         delete state->shutdownAckChunk;
+         delete shutdownCompleteChunk;
+         break;
+      case FORWARD_TSN:
+         sctpEV3 << "SCTPAssociationRcvMessage: FORWARD_TSN received" << endl;
+         SCTPForwardTsnChunk* forwChunk;
+         forwChunk = check_and_cast<SCTPForwardTsnChunk*>(header);
+         processForwardTsnArrived(forwChunk);
+         trans             = true;
+         sendAllowed       = true;
+         dataChunkReceived = true;
+         delete forwChunk;
+         break;
+      case STREAM_RESET:
+         sctpEV3 << "SCTPAssociationRcvMessage: StreamReset received" << endl;
+         SCTPStreamResetChunk* strResChunk;
+         strResChunk = check_and_cast<SCTPStreamResetChunk*>(header);
+         processStreamResetArrived(strResChunk);
+         trans       = true;
+         sendAllowed = true;
+         delete strResChunk;
+         break;
+      case ASCONF:
+         sctpEV3 << "SCTPAssociationRcvMessage: ASCONF received" << endl;
+         if (fsm->getState() == SCTP_S_COOKIE_ECHOED) {
+            trans=performStateTransition(SCTP_E_RCV_COOKIE_ACK);
+         }
+         SCTPAsconfChunk* asconfChunk;
+         asconfChunk = check_and_cast<SCTPAsconfChunk*>(header);
+         processAsconfArrived(asconfChunk);
+         trans = true;
+         delete asconfChunk;
+         break;
+      case ASCONF_ACK:
+         sctpEV3 << "SCTPAssociationRcvMessage: ASCONF_ACK received" << endl;
+         SCTPAsconfAckChunk* asconfAckChunk;
+         asconfAckChunk = check_and_cast<SCTPAsconfAckChunk*>(header);
+         processAsconfAckArrived(asconfAckChunk);
+         trans = true;
+         delete state->asconfChunk;
+         delete asconfAckChunk;
+         break;
+      case PKTDROP:
+         sctpEV3 << "SCTPAssociationRcvMessage: PKTDROP received" << endl;
+         if (sctpMain->pktdrop) {
+            SCTPPacketDropChunk* packetDropChunk;
+            packetDropChunk = check_and_cast<SCTPPacketDropChunk*>(header);
+            if (packetDropChunk->getBFlag() && !packetDropChunk->getMFlag())
+               processPacketDropArrived(packetDropChunk);
+
+            trans       = true;
+            sendAllowed = true;
+            delete packetDropChunk;
+         }
+         break;
+      case ERRORTYPE:
+         sctpEV3 << "SCTPAssociationRcvMessage: ERROR received" << endl;
+         SCTPErrorChunk* errorChunk;
+         errorChunk = check_and_cast<SCTPErrorChunk*>(header);
+         processErrorArrived(errorChunk);
+         trans = true;
+         delete errorChunk;
+         break;
+      default: sctpEV3<<"different type" << endl;
+         break;
+      }
+
+      if (i==numberOfChunks-1 && (dataChunkReceived || dupReceived)) {
+         sendAllowed=true;
+         sctpEV3 << "i=" << i << " sendAllowed=true; scheduleSack" << endl;
+         scheduleSack();
+         if (fsm->getState() == SCTP_S_SHUTDOWN_SENT && state->ackState>=sackFrequency) {
+            sendSack();
+         }
+      }
+
+      // Send any new DATA chunks, SACK chunks, HEARTBEAT chunks etc.
+      sctpEV3 << "SCTPAssociationRcvMessage: send new data? state=" << stateName(fsm->getState())
+              << " sendAllowed="    << sendAllowed
+              << " shutdownCalled=" << shutdownCalled << endl;
+      if (((fsm->getState() == SCTP_S_ESTABLISHED) ||
+           (fsm->getState() == SCTP_S_SHUTDOWN_PENDING) ||
+           (fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED)) &&
+          (sendAllowed) &&
+          (!shutdownCalled)) {
+         sendOnAllPaths(state->getPrimaryPath());
+      }
+   }
+
+   // ====== Clean-up =======================================================
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+   if (!state->pktDropSent) {
+      disposeOf(state->sctpmsg);
+      sctpEV3 << "state->sctpmsg was disposed" << endl;
+   }
+   return trans;
 }
 
 bool SCTPAssociation::processInitArrived(SCTPInitChunk* initchunk, int32 srcPort, int32 destPort)
 {
-    SCTPAssociation* assoc;
-    char timerName[128];
-    bool trans = false;
-    InterfaceTableAccess interfaceTableAccess;
-    AddressVector adv;
-    sctpEV3<<"processInitArrived\n";
-    if (fsm->getState() == SCTP_S_CLOSED)
-    {
-        sctpEV3<<"fork="<<state->fork<<" initReceived="<<state->initReceived<<"\n";
-        if (state->fork && !state->initReceived)
-        {
-            sctpEV3<<"cloneAssociation\n";
-            assoc = cloneAssociation();
-            sctpEV3<<"addForkedAssociation\n";
-            sctpMain->addForkedAssociation(this, assoc, localAddr, remoteAddr, srcPort, destPort);
-
-            sctpEV3 << "Connection forked: this connection got new assocId=" << assocId << ", "
-                "spinoff keeps LISTENing with assocId=" << assoc->assocId << "\n";
-            snprintf(timerName, sizeof(timerName), "T2_SHUTDOWN of assoc %d", assocId);
-            T2_ShutdownTimer->setName(timerName);
-            snprintf(timerName, sizeof(timerName), "T5_SHUTDOWN_GUARD of assoc %d", assocId);
-            T5_ShutdownGuardTimer->setName(timerName);
-            snprintf(timerName, sizeof(timerName), "SACK_TIMER of assoc %d", assocId);
-            SackTimer->setName(timerName);
-            snprintf(timerName, sizeof(timerName), "T1_INIT of assoc %d", assocId);
-            T1_InitTimer->setName(timerName);
-        }
-        else
-        {
-            sctpMain->updateSockPair(this, localAddr, remoteAddr, srcPort, destPort);
-
-        }
-        if (!state->initReceived)
-        {
-            state->initReceived = true;
-            state->initialPrimaryPath = remoteAddr;
-            state->setPrimaryPath(getPath(remoteAddr));
-            if (initchunk->getAddressesArraySize()==0)
-            {
-                sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<"\n";
-                SCTPPathVariables* rPath = new SCTPPathVariables(remoteAddr, this);
-                sctpPathMap[rPath->remoteAddress] = rPath;
-                qCounter.roomTransQ[rPath->remoteAddress]     = 0;
-                qCounter.bookedTransQ[rPath->remoteAddress] = 0;
-                qCounter.roomRetransQ[rPath->remoteAddress] = 0;
-            }
-            initPeerTsn=initchunk->getInitTSN();
-            state->cTsnAck = initPeerTsn - 1;
-            state->initialPeerRwnd = initchunk->getA_rwnd();
-            state->peerRwnd = state->initialPeerRwnd;
-            localVTag= initchunk->getInitTag();
-            numberOfRemoteAddresses =initchunk->getAddressesArraySize();
-            IInterfaceTable *ift = interfaceTableAccess.get();
-            state->localAddresses.clear();
-            if (localAddressList.front() == IPvXAddress("0.0.0.0"))
+   SCTPAssociation* assoc;
+   char             timerName[128];
+   uint16           type;
+   bool             trans = false;
+   InterfaceTableAccess interfaceTableAccess;
+   AddressVector        adv;
+
+   sctpEV3<<"processInitArrived\n";
+   if (fsm->getState() == SCTP_S_CLOSED)
+   {
+      sctpEV3<<"fork="<<state->fork<<" initReceived="<<state->initReceived<<"\n";
+      if (state->fork && !state->initReceived)
+      {
+         sctpEV3<<"cloneAssociation\n";
+         assoc = cloneAssociation();
+         sctpEV3<<"addForkedAssociation\n";
+         sctpMain->addForkedAssociation(this, assoc, localAddr, remoteAddr, srcPort, destPort);
+
+         sctpEV3 << "Connection forked: this connection got new assocId=" << assocId << ", "
+            "spinoff keeps LISTENing with assocId=" << assoc->assocId << "\n";
+         snprintf(timerName, sizeof(timerName), "T2_SHUTDOWN of assoc %d", assocId);
+         T2_ShutdownTimer->setName(timerName);
+         snprintf(timerName, sizeof(timerName), "T5_SHUTDOWN_GUARD of assoc %d", assocId);
+         T5_ShutdownGuardTimer->setName(timerName);
+         snprintf(timerName, sizeof(timerName), "SACK_TIMER of assoc %d", assocId);
+         SackTimer->setName(timerName);
+         snprintf(timerName, sizeof(timerName), "T1_INIT of assoc %d", assocId);
+         T1_InitTimer->setName(timerName);
+      }
+      else
+      {
+         sctpMain->updateSockPair(this, localAddr, remoteAddr, srcPort, destPort);
+
+      }
+      if (!state->initReceived)
+      {
+         state->initReceived = true;
+         state->initialPrimaryPath = remoteAddr;
+         state->setPrimaryPath(getPath(remoteAddr));
+         if (initchunk->getAddressesArraySize()==0)
+         {
+            sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<"\n";
+            SCTPPathVariables* rPath = new SCTPPathVariables(remoteAddr, this);
+            sctpPathMap[rPath->remoteAddress] = rPath;
+            qCounter.roomTransQ[rPath->remoteAddress]   = 0;
+            qCounter.bookedTransQ[rPath->remoteAddress] = 0;
+            qCounter.roomRetransQ[rPath->remoteAddress] = 0;
+         }
+         initPeerTsn=initchunk->getInitTSN();
+         state->gapList.setInitialCumAckTSN(initPeerTsn - 1);
+         state->initialPeerRwnd = initchunk->getA_rwnd();
+         if (initchunk->getMsg_rwnd()>0)
+         {
+            state->peerAllowsChunks = true;
+            state->initialPeerMsgRwnd = initchunk->getMsg_rwnd();
+            state->peerMsgRwnd = state->initialPeerMsgRwnd;
+         }
+         state->peerRwnd = state->initialPeerRwnd;
+         statisticsPeerRwnd->record(state->peerRwnd);
+         localVTag= initchunk->getInitTag();
+         numberOfRemoteAddresses =initchunk->getAddressesArraySize();
+         IInterfaceTable *ift = interfaceTableAccess.get();
+         state->localAddresses.clear();
+         if (localAddressList.front() == IPvXAddress("0.0.0.0"))
+         {
+            for (int32 i=0; i<ift->getNumInterfaces(); ++i)
             {
-                for (int32 i=0; i<ift->getNumInterfaces(); ++i)
-                {
-                    if (ift->getInterface(i)->ipv4Data()!=NULL)
-                    {
-                        adv.push_back(ift->getInterface(i)->ipv4Data()->getIPAddress());
-                    }
-                    else if (ift->getInterface(i)->ipv6Data()!=NULL)
-                    {
-                        adv.push_back(ift->getInterface(i)->ipv6Data()->getAddress(0));
-                    }
-                }
+               if (ift->getInterface(i)->ipv4Data()!=NULL)
+               {
+                  adv.push_back(ift->getInterface(i)->ipv4Data()->getIPAddress());
+               }
+               else if (ift->getInterface(i)->ipv6Data()!=NULL)
+               {
+                  adv.push_back(ift->getInterface(i)->ipv6Data()->getAddress(0));
+               }
             }
-            else
+         }
+         else
+         {
+            adv = localAddressList;
+         }
+         uint32 rlevel = getLevel(remoteAddr);
+         if (rlevel>0)
+            for (AddressVector::iterator i=adv.begin(); i!=adv.end(); ++i)
             {
-                adv = localAddressList;
+               if (getLevel((*i))>=rlevel)
+               {
+                  sctpMain->addLocalAddress(this, (*i));
+                  state->localAddresses.push_back((*i));
+               }
             }
-            uint32 rlevel = getLevel(remoteAddr);
-            if (rlevel>0)
-                for (AddressVector::iterator i=adv.begin(); i!=adv.end(); ++i)
-                {
-                    if (getLevel((*i))>=rlevel)
-                    {
-                        sctpMain->addLocalAddress(this, (*i));
-                        state->localAddresses.push_back((*i));
-                    }
-                }
-            for (uint32 j=0; j<initchunk->getAddressesArraySize(); j++)
+         for (uint32 j=0; j<initchunk->getAddressesArraySize(); j++)
+         {
+            // skip IPv6 because we can't send to them yet
+            if (initchunk->getAddresses(j).isIPv6())
+               continue;
+            // set path variables for this pathlocalAddresses
+            if (!getPath(initchunk->getAddresses(j)))
             {
-                // skip IPv6 because we can't send to them yet
-                if (initchunk->getAddresses(j).isIPv6())
-                    continue;
-                // set path variables for this pathlocalAddresses
-                if (!getPath(initchunk->getAddresses(j)))
-                {
-                    SCTPPathVariables* path = new SCTPPathVariables(initchunk->getAddresses(j), this);
-                    sctpEV3<<__LINE__<<" get new path for "<<initchunk->getAddresses(j)<<" ptr="<<path<<"\n";
-                    for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k)
-                    {
-                        sctpMain->addRemoteAddress(this,(*k), initchunk->getAddresses(j));
-                        this->remoteAddressList.push_back(initchunk->getAddresses(j));
-                    }
-                    sctpPathMap[path->remoteAddress] = path;
-                    qCounter.roomTransQ[path->remoteAddress]     = 0;
-                    qCounter.bookedTransQ[path->remoteAddress] = 0;
-                    qCounter.roomRetransQ[path->remoteAddress] = 0;
-                }
+               SCTPPathVariables* path = new SCTPPathVariables(initchunk->getAddresses(j), this);
+               sctpEV3<<__LINE__<<" get new path for "<<initchunk->getAddresses(j)<<" ptr="<<path<<"\n";
+               for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k)
+               {
+                  sctpMain->addRemoteAddress(this,(*k), initchunk->getAddresses(j));
+                  this->remoteAddressList.push_back(initchunk->getAddresses(j));
+               }
+               sctpPathMap[path->remoteAddress] = path;
+               qCounter.roomTransQ[path->remoteAddress]   = 0;
+               qCounter.bookedTransQ[path->remoteAddress] = 0;
+               qCounter.roomRetransQ[path->remoteAddress] = 0;
             }
-            SCTPPathMap::iterator ite=sctpPathMap.find(remoteAddr);
-            if (ite==sctpPathMap.end())
+         }
+         SCTPPathMap::iterator ite=sctpPathMap.find(remoteAddr);
+         if (ite==sctpPathMap.end())
+         {
+            SCTPPathVariables* path = new SCTPPathVariables(remoteAddr, this);
+            sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<" ptr="<<path<<"\n";
+            sctpPathMap[remoteAddr] = path;
+            qCounter.roomTransQ[remoteAddr]   = 0;
+            qCounter.bookedTransQ[remoteAddr] = 0;
+            qCounter.roomRetransQ[remoteAddr] = 0;
+         }
+         if (initchunk->getHmacTypesArraySize() != 0)
+         {
+            state->peerAuth = true;
+            for (uint32 j=0; j<initchunk->getChunkTypesArraySize();j++)
             {
-                SCTPPathVariables* path = new SCTPPathVariables(remoteAddr, this);
-                sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<" ptr="<<path<<"\n";
-                sctpPathMap[remoteAddr] = path;
-                qCounter.roomTransQ[remoteAddr]  = 0;
-                qCounter.bookedTransQ[remoteAddr] = 0;
-                qCounter.roomRetransQ[remoteAddr] = 0;
+               type = initchunk->getChunkTypes(j);
+               if (type != INIT && type != INIT_ACK && type != AUTH && type != SHUTDOWN_COMPLETE)
+               {
+                  state->peerChunkList.push_back(type);
+               }
             }
-            trans = performStateTransition(SCTP_E_RCV_INIT);
-            if (trans) {
-                sendInitAck(initchunk);
-            }
-        }
-        else if (fsm->getState() == SCTP_S_CLOSED)
-        {
-            trans=performStateTransition(SCTP_E_RCV_INIT);
-            if (trans) {
-                sendInitAck(initchunk);
-            }
-        }
-        else {
-            trans = true;
-        }
-    }
-    else if (fsm->getState() == SCTP_S_COOKIE_WAIT) //INIT-Collision
-    {
-        sctpEV3<<"INIT collision: send Init-Ack\n";
-        sendInitAck(initchunk);
-        trans=true;
-    }
-    else if (fsm->getState() == SCTP_S_COOKIE_ECHOED || fsm->getState() == SCTP_S_ESTABLISHED)
-    {
-        // check, whether a new address has been added
-        bool addressPresent = false;
-        for (uint32 j=0; j<initchunk->getAddressesArraySize(); j++)
-        {
-            if (initchunk->getAddresses(j).isIPv6())
-                continue;
-            for (AddressVector::iterator k=remoteAddressList.begin(); k!=remoteAddressList.end(); ++k)
+         }
+         sctpEV3<<"number supported extensions:"<<initchunk->getSepChunksArraySize()<<"\n";
+         if (initchunk->getSepChunksArraySize() > 0)
+         {
+            for (uint32 i=0; i<initchunk->getSepChunksArraySize(); i++)
             {
-                if ((*k)==(initchunk->getAddresses(j)))
-                {
-                    addressPresent = true;
-                    break;
-                }
+               if (initchunk->getSepChunks(i) == STREAM_RESET)
+               {
+                  state->peerStreamReset=true;
+                  //break;
+               }
+               if (initchunk->getSepChunks(i) == PKTDROP)
+               {
+                  state->peerPktDrop=true;
+                  sctpEV3<<"set peerPktDrop to true\n";
+                  //break;
+               }
             }
-            if (!addressPresent)
+         }
+         trans = performStateTransition(SCTP_E_RCV_INIT);
+         if (trans) {
+            sendInitAck(initchunk);
+         }
+      }
+      else if (fsm->getState() == SCTP_S_CLOSED)
+      {
+         trans=performStateTransition(SCTP_E_RCV_INIT);
+         if (trans) {
+            sendInitAck(initchunk);
+         }
+      }
+      else {
+         trans = true;
+      }
+   }
+   else if (fsm->getState() == SCTP_S_COOKIE_WAIT) //INIT-Collision
+   {
+      sctpEV3<<"INIT collision: send Init-Ack\n";
+      sendInitAck(initchunk);
+      trans=true;
+   }
+   else if (fsm->getState() == SCTP_S_COOKIE_ECHOED || fsm->getState() == SCTP_S_ESTABLISHED)
+   {
+      // check, whether a new address has been added
+      bool addressPresent = false;
+      for (uint32 j=0; j<initchunk->getAddressesArraySize(); j++)
+      {
+         if (initchunk->getAddresses(j).isIPv6())
+            continue;
+         for (AddressVector::iterator k=remoteAddressList.begin(); k!=remoteAddressList.end(); ++k)
+         {
+            if ((*k)==(initchunk->getAddresses(j)))
             {
-                sendAbort();
-                return true;
+               addressPresent = true;
+               break;
             }
-        }
-        sendInitAck(initchunk);
-        trans=true;
-    }
-    else if (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)
-        trans = true;
-    printSctpPathMap();
-    return trans;
+         }
+         if (!addressPresent)
+         {
+            sendAbort();
+            return true;
+         }
+      }
+      sendInitAck(initchunk);
+      trans=true;
+   }
+   else if (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)
+      trans = true;
+   printSctpPathMap();
+   return trans;
 }
 
 
 bool SCTPAssociation::processInitAckArrived(SCTPInitAckChunk* initAckChunk)
 {
-    bool trans = false;
-    if (fsm->getState() == SCTP_S_COOKIE_WAIT)
-    {
-        sctpEV3<<"State is COOKIE_WAIT, Cookie_Echo has to be sent\n";
-        stopTimer(T1_InitTimer);
-        state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT;
-        trans=performStateTransition(SCTP_E_RCV_INIT_ACK);
-        //delete state->initChunk; will be deleted when state ESTABLISHED is entered
-        if (trans)
-        {
-            state->initialPrimaryPath = remoteAddr;
-            state->setPrimaryPath(getPath(remoteAddr));
-            initPeerTsn=initAckChunk->getInitTSN();
-            localVTag= initAckChunk->getInitTag();
-            state->cTsnAck = initPeerTsn - 1;
-            state->initialPeerRwnd = initAckChunk->getA_rwnd();
-            state->peerRwnd = state->initialPeerRwnd;
-            remoteAddressList.clear();
-            numberOfRemoteAddresses =initAckChunk->getAddressesArraySize();
-            sctpEV3<<"number of remote addresses in initAck="<<numberOfRemoteAddresses<<"\n";
-            for (uint32 j=0; j<numberOfRemoteAddresses; j++)
+   bool   trans = false;
+   uint16 type;
+
+   if (fsm->getState() == SCTP_S_COOKIE_WAIT)
+   {
+      sctpEV3<<"State is COOKIE_WAIT, Cookie_Echo has to be sent\n";
+      stopTimer(T1_InitTimer);
+      state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT;
+      trans=performStateTransition(SCTP_E_RCV_INIT_ACK);
+      //delete state->initChunk; will be deleted when state ESTABLISHED is entered
+      if (trans)
+      {
+         state->initialPrimaryPath = remoteAddr;
+         state->setPrimaryPath(getPath(remoteAddr));
+         initPeerTsn=initAckChunk->getInitTSN();
+         localVTag= initAckChunk->getInitTag();
+         state->gapList.setInitialCumAckTSN(initPeerTsn - 1);
+         state->initialPeerRwnd = initAckChunk->getA_rwnd();
+         state->peerRwnd = state->initialPeerRwnd;
+         statisticsPeerRwnd->record(state->peerRwnd);
+         if (initAckChunk->getMsg_rwnd()>0)
+         {
+            state->peerAllowsChunks = true;
+            state->initialPeerMsgRwnd = initAckChunk->getMsg_rwnd();
+            state->peerMsgRwnd = state->initialPeerMsgRwnd;
+         }
+         state->expectedStreamResetSequenceNumber = initPeerTsn;
+         remoteAddressList.clear();
+         numberOfRemoteAddresses =initAckChunk->getAddressesArraySize();
+         sctpEV3<<"number of remote addresses in initAck="<<numberOfRemoteAddresses<<"\n";
+         for (uint32 j=0; j<numberOfRemoteAddresses; j++)
+         {
+            if (initAckChunk->getAddresses(j).isIPv6())
+               continue;
+            for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k)
             {
-                if (initAckChunk->getAddresses(j).isIPv6())
-                    continue;
-                for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k)
-                {
-                    if (!((*k).isUnspecified()))
-                    {
-                        sctpEV3<<"addPath "<<initAckChunk->getAddresses(j)<<"\n";
-                        sctpMain->addRemoteAddress(this,(*k), initAckChunk->getAddresses(j));
-                        this->remoteAddressList.push_back(initAckChunk->getAddresses(j));
-                        addPath(initAckChunk->getAddresses(j));
-                    }
-                }
+               if (!((*k).isUnspecified()))
+               {
+                  sctpEV3<<"addPath "<<initAckChunk->getAddresses(j)<<"\n";
+                  sctpMain->addRemoteAddress(this,(*k), initAckChunk->getAddresses(j));
+                  this->remoteAddressList.push_back(initAckChunk->getAddresses(j));
+                  addPath(initAckChunk->getAddresses(j));
+               }
             }
-            SCTPPathMap::iterator ite=sctpPathMap.find(remoteAddr);
-            if (ite==sctpPathMap.end())
+         }
+         SCTPPathMap::iterator ite=sctpPathMap.find(remoteAddr);
+         if (ite==sctpPathMap.end())
+         {
+            sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<"\n";
+            SCTPPathVariables* path = new SCTPPathVariables(remoteAddr, this);
+            sctpPathMap[remoteAddr] = path;
+            qCounter.roomTransQ[remoteAddr]   = 0;
+            qCounter.roomRetransQ[remoteAddr] = 0;
+            qCounter.bookedTransQ[remoteAddr] = 0;
+         }
+         inboundStreams  = ((initAckChunk->getNoOutStreams()<inboundStreams)?initAckChunk->getNoOutStreams():inboundStreams);
+         outboundStreams = ((initAckChunk->getNoInStreams()<outboundStreams)?initAckChunk->getNoInStreams():outboundStreams);
+         (this->*ssFunctions.ssInitStreams)(inboundStreams, outboundStreams);
+         if (initAckChunk->getHmacTypesArraySize() != 0)
+         {
+            state->peerAuth = true;
+            for (uint32 j=0; j<initAckChunk->getChunkTypesArraySize();j++)
             {
-                sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<"\n";
-                SCTPPathVariables* path = new SCTPPathVariables(remoteAddr, this);
-                sctpPathMap[remoteAddr] = path;
-                qCounter.roomTransQ[remoteAddr]  = 0;
-                qCounter.roomRetransQ[remoteAddr] = 0;
-                qCounter.bookedTransQ[remoteAddr] = 0;
+               type = initAckChunk->getChunkTypes(j);
+               if (type != INIT && type != INIT_ACK && type != AUTH && type != SHUTDOWN_COMPLETE)
+               {
+                  state->peerChunkList.push_back(type);
+               }
             }
-            inboundStreams   = ((initAckChunk->getNoOutStreams()<inboundStreams)?initAckChunk->getNoOutStreams():inboundStreams);
-            outboundStreams = ((initAckChunk->getNoInStreams()<outboundStreams)?initAckChunk->getNoInStreams():outboundStreams);
-            (this->*ssFunctions.ssInitStreams)(inboundStreams, outboundStreams);
-            sendCookieEcho(initAckChunk);
-        }
-        startTimer(T1_InitTimer, state->initRexmitTimeout);
-    }
-    else
-        sctpEV3<<"State="<<fsm->getState()<<"\n";
-    printSctpPathMap();
-    return trans;
+
+         }
+         if (initAckChunk->getSepChunksArraySize() > 0)
+         {
+            for (uint32 i=0; i<initAckChunk->getSepChunksArraySize(); i++)
+            {
+               if (initAckChunk->getSepChunks(i) == STREAM_RESET)
+               {
+                  state->peerStreamReset=true;
+                  break;
+               }
+               if (initAckChunk->getSepChunks(i) == PKTDROP)
+               {
+                  state->peerPktDrop=true;
+                  break;
+               }
+            }
+         }
+         sendCookieEcho(initAckChunk);
+      }
+      startTimer(T1_InitTimer, state->initRexmitTimeout);
+   }
+   else
+      sctpEV3<<"State="<<fsm->getState()<<"\n";
+   printSctpPathMap();
+   return trans;
 }
 
 
 
 bool SCTPAssociation::processCookieEchoArrived(SCTPCookieEchoChunk* cookieEcho, IPvXAddress addr)
 {
-    bool trans = false;
-    SCTPCookie* cookie = check_and_cast<SCTPCookie*>(cookieEcho->getStateCookie());
-    if (cookie->getCreationTime()+(int32)sctpMain->par("validCookieLifetime")<simTime())
-    {
-        sctpEV3<<"stale Cookie: sendAbort\n";
-        sendAbort();
-        delete cookie;
-        return trans;
-    }
-    if (fsm->getState() == SCTP_S_CLOSED)
-    {
-        if (cookie->getLocalTag()!=localVTag || cookie->getPeerTag() != peerVTag)
-        {
-            bool same=true;
-            for (int32 i=0; i<32; i++)
+   bool trans = false;
+   SCTPCookie* cookie = check_and_cast<SCTPCookie*>(cookieEcho->getStateCookie());
+   if (cookie->getCreationTime()+(int32)sctpMain->par("validCookieLifetime")<simTime())
+   {
+      sctpEV3<<"stale Cookie: sendAbort\n";
+      sendAbort();
+      delete cookie;
+      return trans;
+   }
+   if (fsm->getState() == SCTP_S_CLOSED)
+   {
+      if (cookie->getLocalTag()!=localVTag || cookie->getPeerTag() != peerVTag)
+      {
+         bool same=true;
+         for (int32 i=0; i<32; i++)
+         {
+            if (cookie->getLocalTieTag(i)!=state->localTieTag[i])
             {
-                if (cookie->getLocalTieTag(i)!=state->localTieTag[i])
-                {
-                    same = false;
-                    break;
-                }
-                if (cookie->getPeerTieTag(i)!=state->peerTieTag[i])
-                {
-                    same = false;
-                    break;
-                }
+               same = false;
+               break;
             }
-            if (!same)
+            if (cookie->getPeerTieTag(i)!=state->peerTieTag[i])
             {
-                sendAbort();
-                delete cookie;
-                return trans;
+               same = false;
+               break;
             }
-        }
-        sctpEV3<<"State is CLOSED, Cookie_Ack has to be sent\n";
-        trans=performStateTransition(SCTP_E_RCV_VALID_COOKIE_ECHO);
-        if (trans)
-            sendCookieAck(addr);//send to address
-    }
-    else if (fsm->getState() == SCTP_S_ESTABLISHED || fsm->getState() == SCTP_S_COOKIE_WAIT || fsm->getState() == SCTP_S_COOKIE_ECHOED)
-    {
-        sctpEV3<<"State is not CLOSED, but COOKIE_ECHO received. Compare the Tags\n";
-        // case A: Peer restarted, retrieve information from cookie
-        if (cookie->getLocalTag()!=localVTag && cookie->getPeerTag() != peerVTag )
-        {
-            bool same=true;
-            for (int32 i=0; i<32; i++)
+         }
+         if (!same)
+         {
+            sendAbort();
+            delete cookie;
+            return trans;
+         }
+      }
+      sctpEV3<<"State is CLOSED, Cookie_Ack has to be sent\n";
+      trans=performStateTransition(SCTP_E_RCV_VALID_COOKIE_ECHO);
+      if (trans)
+         sendCookieAck(addr);//send to address
+   }
+   else if (fsm->getState() == SCTP_S_ESTABLISHED || fsm->getState() == SCTP_S_COOKIE_WAIT || fsm->getState() == SCTP_S_COOKIE_ECHOED)
+   {
+      sctpEV3<<"State is not CLOSED, but COOKIE_ECHO received. Compare the Tags\n";
+      // case A: Peer restarted, retrieve information from cookie
+      if (cookie->getLocalTag()!=localVTag && cookie->getPeerTag() != peerVTag )
+      {
+         bool same=true;
+         for (int32 i=0; i<32; i++)
+         {
+            if (cookie->getLocalTieTag(i)!=state->localTieTag[i])
             {
-                if (cookie->getLocalTieTag(i)!=state->localTieTag[i])
-                {
-                    same = false;
-                    break;
-                }
-                if (cookie->getPeerTieTag(i)!=state->peerTieTag[i])
-                {
-                    same = false;
-                    break;
-                }
+               same = false;
+               break;
             }
-            if (same)
+            if (cookie->getPeerTieTag(i)!=state->peerTieTag[i])
             {
-                localVTag = cookie->getLocalTag();
-                peerVTag = cookie->getPeerTag();
-                sendCookieAck(addr);
+               same = false;
+               break;
             }
-        }
-        // case B: Setup collision, retrieve information from cookie
-        else if (cookie->getPeerTag()==peerVTag && (cookie->getLocalTag()!=localVTag || cookie->getLocalTag()==0))
-        {
+         }
+         if (same)
+         {
             localVTag = cookie->getLocalTag();
             peerVTag = cookie->getPeerTag();
-            performStateTransition(SCTP_E_RCV_VALID_COOKIE_ECHO);
             sendCookieAck(addr);
-        }
-        else if (cookie->getPeerTag()==peerVTag && cookie->getLocalTag()==localVTag)
-        {
-            sendCookieAck(addr); //send to address src
-        }
-        trans=true;
-    }
-    else
-    {
-        sctpEV3<<"State="<<fsm->getState()<<"\n";
-        trans = true;
-    }
-    delete cookie;
-    return trans;
+         }
+      }
+      // case B: Setup collision, retrieve information from cookie
+      else if (cookie->getPeerTag()==peerVTag && (cookie->getLocalTag()!=localVTag || cookie->getLocalTag()==0))
+      {
+         localVTag = cookie->getLocalTag();
+         peerVTag = cookie->getPeerTag();
+         performStateTransition(SCTP_E_RCV_VALID_COOKIE_ECHO);
+         sendCookieAck(addr);
+      }
+      else if (cookie->getPeerTag()==peerVTag && cookie->getLocalTag()==localVTag)
+      {
+         sendCookieAck(addr); //send to address src
+      }
+      trans=true;
+   }
+   else
+   {
+      sctpEV3<<"State="<<fsm->getState()<<"\n";
+      trans = true;
+   }
+   delete cookie;
+   return trans;
 }
 
 bool SCTPAssociation::processCookieAckArrived()
 {
-    bool trans=false;
-
-    if (fsm->getState() == SCTP_S_COOKIE_ECHOED)
-    {
-        stopTimer(T1_InitTimer);
-        trans=performStateTransition(SCTP_E_RCV_COOKIE_ACK);
-        if (state->cookieChunk->getCookieArraySize()==0)
-        {
-                delete state->cookieChunk->getStateCookie();
-        }
-        delete state->cookieChunk;
-        return trans;
-    }
-    else
-        sctpEV3<<"State="<<fsm->getState()<<"\n";
-
-    return trans;
+   bool trans=false;
+
+   if (fsm->getState() == SCTP_S_COOKIE_ECHOED)
+   {
+      stopTimer(T1_InitTimer);
+      trans=performStateTransition(SCTP_E_RCV_COOKIE_ACK);
+      if (state->cookieChunk->getCookieArraySize()==0)
+      {
+            delete state->cookieChunk->getStateCookie();
+      }
+      delete state->cookieChunk;
+      return trans;
+   }
+   else
+      sctpEV3<<"State="<<fsm->getState()<<"\n";
+
+   return trans;
 }
 
 void SCTPAssociation::tsnWasReneged(SCTPDataVariables*       chunk,
-                                                const int                    type)
+                                    const SCTPPathVariables* sackPath,
+                                    const int                type)
 {
 
-#ifdef PKT
-    if (transmissionQ->getQueueSize() > 0) {
-        for (SCTPQueue::PayloadQueue::iterator tq = transmissionQ->payloadQueue.begin();
-              tq != transmissionQ->payloadQueue.end(); tq++) {
-            if (tq->second->tsn > chunk->tsn) {
-                if (!transmissionQ->checkAndInsertChunk(chunk->tsn, chunk)) {
-                    sctpEV3 << "tsnWasReneged: cannot add message/chunk (TSN="
-                              << chunk->tsn <<") to the transmissionQ" << endl;
-                }
-                else {
-                    chunk->enqueuedInTransmissionQ = true;
-                    chunk->setNextDestination(chunk->getLastDestinationPath());
-                    CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination());
-                    q->second+=ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH);
-                    CounterMap::iterator qb=qCounter.bookedTransQ.find(chunk->getNextDestination());
-                    qb->second+=chunk->booksize;
-                    return;
-                }
-            }
-        }
-    }
-#endif
-    sctpEV3 << "TSN " << chunk->tsn << " has been reneged (type "
-              << type << ")" << endl;
-    unackChunk(chunk);
-    if (chunk->countsAsOutstanding) {
-        decreaseOutstandingBytes(chunk);
-    }
-    chunk->hasBeenReneged = true;
-    chunk->gapReports        = 1;
-    if (!chunk->getLastDestinationPath()->T3_RtxTimer->isScheduled()) {
-        startTimer(chunk->getLastDestinationPath()->T3_RtxTimer,
-                      chunk->getLastDestinationPath()->pathRto);
-    }
+   sctpEV3 << "TSN " << chunk->tsn << " has been reneged (type "
+           << type << ")" << endl;
+   unackChunk(chunk);
+   if (chunk->countsAsOutstanding) {
+      decreaseOutstandingBytes(chunk);
+   }
+   chunk->hasBeenReneged = true;
+   chunk->gapReports     = 1;
+   if (!chunk->getLastDestinationPath()->T3_RtxTimer->isScheduled()) {
+      startTimer(chunk->getLastDestinationPath()->T3_RtxTimer,
+                 chunk->getLastDestinationPath()->pathRto);
+   }
 }
 
 
@@ -708,1035 +915,2124 @@ void SCTPAssociation::tsnWasReneged(SCTPDataVariables*       chunk,
 
 SCTPEventCode SCTPAssociation::processSackArrived(SCTPSackChunk* sackChunk)
 {
-    simtime_t            rttEstimation            = MAXTIME;
-    bool                     ctsnaAdvanced            = false;
-    SCTPPathVariables* path                       = getPath(remoteAddr);    // Path for *this* SACK!
-    const uint64         arwnd                    = sackChunk->getA_rwnd();
-    const uint32         tsna                         = sackChunk->getCumTsnAck();
-    uint32               highestNewAck            = tsna;   // Highest newly acked TSN
-    const uint16         numGaps                      = sackChunk->getNumGaps();
-    bool                     getChunkFastFirstTime = true;
-
-    // ====== Print some information =========================================
-    sctpEV3 << "##### SACK Processing: TSNa=" << tsna << " #####" << endl;
-    for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
-        SCTPPathVariables* myPath = piter->second;
-        sctpEV3 << "Path " << myPath->remoteAddress << ":\t"
-                  << "outstanding="          << path->outstandingBytes << "\t"
-                  << "T3scheduled="          << path->T3_RtxTimer->getArrivalTime() << " "
-                  << (path->T3_RtxTimer->isScheduled() ? "[ok]" : "[NOT SCHEDULED]") << "\t"
-                  << endl;
-    }
-
-
-
-    // ====== Initialize some variables ======================================
-    for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
-        SCTPPathVariables* myPath = piter->second;
-        // T.D. 26.03.09: Remember outstanding bytes before this update
-        // Values are necessary for updating the congestion window!
-        myPath->outstandingBytesBeforeUpdate = myPath->outstandingBytes;     // T.D. 14.11.09: Bugfix - copy from myPath, not from path!
-        myPath->requiresRtx                      = false;
-        myPath->lowestTSNRetransmitted       = false;
-        myPath->findLowestTSN                    = true;
-        myPath->gapAcksInLastSACK                = 0;
-        myPath->gapNAcksInLastSACK               = 0;
-        myPath->newlyAckedBytes                  = 0;
-        if (myPath == path) {
-            myPath->lastAckTime = simTime();
-        }
-    }
+   simtime_t          rttEstimation          = MAXTIME;
+   const uint64       sendBufferBeforeUpdate = state->sendBuffer;
+   SCTPPathVariables* path                   = getPath(remoteAddr);   // Path for *this* SACK!
+   const uint64       arwnd                  = sackChunk->getA_rwnd();
+   const uint32       tsna                   = sackChunk->getCumTsnAck();
+   uint32             highestNewAck          = tsna;   // Highest newly acked TSN
+   const uint16       numDups                = sackChunk->getNumDupTsns();
+   SCTP::AssocStat*   assocStat              = sctpMain->getAssocStat(assocId);
+   bool               dropFilledGap          = false;
+   const uint32       msgRwnd                = sackChunk->getMsg_rwnd();
+
+   // ====== Put information from SACK into GapList =========================
+   GapList sackGapList;
+   sackGapList.setInitialCumAckTSN(sackChunk->getCumTsnAck());
+   uint32 lastTSN = sackChunk->getCumTsnAck();
+   for(uint32 i = 0;i < sackChunk->getNumGaps(); i++) {
+      uint32 tsn = sackChunk->getGapStart(i);
+      assert(tsnLt(lastTSN + 1, tsn)); lastTSN = tsn;
+      while(tsnLe(tsn, sackChunk->getGapStop(i))) {
+         bool dummy;
+         sackGapList.updateGapList(tsn, dummy, true);    // revokable TSN
+         tsn++;
+      }
+      lastTSN = sackChunk->getGapStop(i);
+   }
+   assocStat->sumRGapRanges += ((sackChunk->getCumTsnAck() <= lastTSN) ?
+      (uint64)(lastTSN - sackChunk->getCumTsnAck()) :
+      (uint64)(sackChunk->getCumTsnAck() - lastTSN));
+   if(sackChunk->getNrSubtractRGaps() == false) {
+      lastTSN = sackChunk->getCumTsnAck();
+      for(uint32 i = 0;i < sackChunk->getNumNrGaps(); i++) {
+         uint32 tsn = sackChunk->getNrGapStart(i);
+         assert(tsnLt(lastTSN + 1, tsn)); lastTSN = tsn;
+         while(tsnLe(tsn, sackChunk->getNrGapStop(i))) {
+            bool dummy;
+            sackGapList.updateGapList(tsn, dummy, false);   // non-revokable TSN
+            tsn++;
+         }
+         lastTSN = sackChunk->getNrGapStop(i);
+      }
+   }
+   else {
+      lastTSN = sackChunk->getCumTsnAck();
+      for(uint32 i = 0;i < sackChunk->getNumNrGaps(); i++) {
+         uint32 tsn = sackChunk->getNrGapStart(i);
+         assert(tsnLt(lastTSN + 1, tsn)); lastTSN = tsn;
+         while(tsnLe(tsn, sackChunk->getNrGapStop(i))) {
+            if(sackGapList.tsnIsRevokable(tsn) == false) {
+               bool dummy;
+               sackGapList.updateGapList(tsn, dummy, false);   // non-revokable TSN
+            }
+            tsn++;
+         }
+         lastTSN = sackChunk->getNrGapStop(i);
+      }
+   }
+   assocStat->sumNRGapRanges += (sackChunk->getCumTsnAck() <= lastTSN) ?
+      (uint64)(lastTSN - sackChunk->getCumTsnAck()) :
+      (uint64)(sackChunk->getCumTsnAck() - lastTSN);
+   const uint16 numGaps = sackGapList.getNumGaps(GapList::GT_Any);
+
+
+   // ====== Print some information =========================================
+   sctpEV3 << "##### SACK Processing: TSNa=" << tsna << " #####" << endl;
+   for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
+      SCTPPathVariables* myPath = piter->second;
+      sctpEV3 << "Path " << myPath->remoteAddress << ":\t"
+              << "outstanding="         << path->outstandingBytes << "\t"
+              << "T3scheduled="         << path->T3_RtxTimer->getArrivalTime() << " "
+              << (path->T3_RtxTimer->isScheduled() ? "[ok]" : "[NOT SCHEDULED]") << "\t"
+              << endl;
+   }
+
+   sctpEV3 << "Before processSackArrived for path " << path->remoteAddress
+           << " with tsna=" << tsna << ":" << endl;
+   if(SCTP::checkQueues) {
+      dumpPaths();
+      checkOutstandingBytes();
+   }
+
+   // ====== SACK Sequence Number Check =====================================
+   sctpEV3 << "SACK Seq Number = " << sackChunk->getSackSeqNum() << endl;
+   if( (state->checkSackSeqNumber == true) &&
+       (sackChunk->getSackSeqNum() <= state->incomingSackSeqNum) ) {
+      sctpEV3 << "Out-of-data SACK: " << sackChunk->getSackSeqNum()
+              << " < " << state->incomingSackSeqNum << endl;
+      return SCTP_E_IGNORE;
+   }
+   state->incomingSackSeqNum = sackChunk->getSackSeqNum();
+
+   // ====== Record statistics ==============================================
+   numGapBlocks->record(numGaps);
+   statisticsRevokableGapBlocksInLastSACK->record(sackGapList.getNumGaps(GapList::GT_Revokable));
+   statisticsNonRevokableGapBlocksInLastSACK->record(sackGapList.getNumGaps(GapList::GT_NonRevokable));
+
+   path->vectorPathAckedTSNCumAck->record(tsna);
+   if(assocStat) {
+      assocStat->numDups += numDups;
+   }
+
+   // ====== Initialize some variables ======================================
+   for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
+      SCTPPathVariables* myPath = piter->second;
+      // T.D. 26.03.09: Remember outstanding bytes before this update
+      // Values are necessary for updating the congestion window!
+      myPath->outstandingBytesBeforeUpdate    = myPath->outstandingBytes;   // T.D. 14.11.09: Bugfix - copy from myPath, not from path!
+      myPath->requiresRtx                     = false;
+      myPath->lowestTSNRetransmitted          = false;
+      myPath->findLowestTSN                   = true;
+      myPath->gapAckedChunksInLastSACK        = 0;
+      myPath->gapNRAckedChunksInLastSACK      = 0;
+      myPath->gapUnackedChunksInLastSACK      = 0;
+      myPath->newlyAckedBytes                 = 0;
+      myPath->newCumAck                       = false;   // Check whether CumAck affects this path
+      if (myPath == path) {
+         myPath->lastAckTime = simTime();
+      }
+   }
 
 
-    // ====== Zero Window Probing ============================================
-    if ( (state->zeroWindowProbing) && (arwnd > 0) ) {
-        state->zeroWindowProbing = false;
-    }
+   // ====== Zero Window Probing ============================================
+   if ((state->peerAllowsChunks) && (msgRwnd > 0) && (state->zeroWindowProbing) ) {
+      state->zeroWindowProbing = false;
+   }
+   if ( (state->zeroWindowProbing) && (arwnd > 0) ) {
+      state->zeroWindowProbing = false;
+   }
 
 
-    // #######################################################################
-    // #### Processing of CumAck                                                         ####
-    // #######################################################################
+   // #######################################################################
+   // #### Processing of CumAck                                          ####
+   // #######################################################################
 
-    if (tsnGt(tsna, state->lastTsnAck)) {    // Handle new CumAck
-        sctpEV3 << "===== Handling new CumAck for TSN " << tsna << " =====" << endl;
+   if (tsnGt(tsna, state->lastTsnAck)) {   // Handle new CumAck
+      sctpEV3 << "===== Handling new CumAck for TSN " << tsna << " =====" << endl;
 
+      SCTPQueue::PayloadQueue::iterator pay;
+      SCTPDataVariables* myChunk = retransmissionQ->getChunk(state->lastTsnAck + 1);
+      if ( (myChunk != NULL) && (myChunk->wasPktDropped) &&
+           (myChunk->getLastDestinationPath()->fastRecoveryActive) ) {
+         dropFilledGap = true;
+         sctpEV3 << "TSN " << myChunk->tsn << " filled gap" << endl;
+      }
 
-        // We have got new chunks acked, and our cum ack point is advanced ...
-        // Depending on the parameter osbWithHeader ackedBytes are with or without the header bytes.
-        // T.D. 23.03.09: path->newlyAckedBytes is updated in dequeueAckedChunks()!
-        dequeueAckedChunks(tsna, path, rttEstimation); // chunks with tsn between lastTsnAck and tsna are removed from the transmissionQ and the retransmissionQ; outstandingBytes are decreased
+      // We have got new chunks acked, and our cum ack point is advanced ...
+      // Depending on the parameter osbWithHeader ackedBytes are with or without the header bytes.
+      // T.D. 23.03.09: path->newlyAckedBytes is updated in dequeueAckedChunks()!
+      dequeueAckedChunks(tsna, path, rttEstimation); // chunks with tsn between lastTsnAck and tsna are removed from the transmissionQ and the retransmissionQ; outstandingBytes are decreased
 
-        state->lastTsnAck = tsna;
-        ctsnaAdvanced = true;
-    }
-    else if (tsnLt(tsna, state->lastTsnAck)) {
-        sctpEV3 << "Stale CumAck (" << tsna << " < " << state->lastTsnAck << ")"
-                  << endl;
-        return SCTP_E_IGNORE;
-    }
-
-
-    // ====== Handle reneging ================================================
-    if ((numGaps == 0) && (tsnLt(tsna, state->highestTsnAcked))) {
-        // Reneging, type 0:
-        // In a previous SACK, chunks up to highestTsnAcked have been acked.
-        // This SACK contains a CumAck < highestTsnAcked
-        //      => rereg TSNs from CumAck+1 to highestTsnAcked
-        //      => new highestTsnAcked = CumAck
-        sctpEV3 << "numGaps=0 && tsna " << tsna
-                  << " < highestTsnAcked " << state->highestTsnAcked << endl;
-        uint32 i = state->highestTsnAcked;
-        while (i >= tsna + 1) {
+      state->lastTsnAck = tsna;
+      if (tsnGt(tsna, state->advancedPeerAckPoint)) {
+         state->advancedPeerAckPoint = tsna;
+         state->ackPointAdvanced     = true;
+      }
+      chunkMap->cumAck(tsna);
+
+   }
+   else if (tsnLt(tsna, state->lastTsnAck)) {
+      sctpEV3 << "Stale CumAck (" << tsna << " < " << state->lastTsnAck << ")"
+              << endl;
+      return SCTP_E_IGNORE;
+   }
+
+
+   // ====== Handle reneging ================================================
+   if ((numGaps == 0) && (tsnLt(tsna, state->highestTsnAcked))) {
+      // Reneging, type 0:
+      // In a previous SACK, chunks up to highestTsnAcked have been acked.
+      // This SACK contains a CumAck < highestTsnAcked
+      //    => rereg TSNs from CumAck+1 to highestTsnAcked
+      //    => new highestTsnAcked = CumAck
+      sctpEV3 << "numGaps=0 && tsna "  << tsna
+              << " < highestTsnAcked " << state->highestTsnAcked << endl;
+      uint32 i = state->highestTsnAcked;
+      while (i >= tsna + 1) {
+         SCTPDataVariables* myChunk = retransmissionQ->getChunk(i);
+         if(myChunk) {
+            if(chunkHasBeenAcked(myChunk)) {
+               tsnWasReneged(myChunk, path, 0);
+            }
+         }
+         i--;
+      }
+      state->highestTsnAcked = tsna;
+   }
+
+
+   // #######################################################################
+   // #### Processing of GapAcks                                         ####
+   // #######################################################################
+
+   if ((numGaps > 0) && (!retransmissionQ->payloadQueue.empty()) ) {
+      sctpEV3 << "===== Handling GapAcks after CumTSNAck " << tsna << " =====" << endl;
+      sctpEV3 << "We got " << numGaps << " gap reports" << endl;
+
+      // We got fragment reports... check for newly acked chunks.
+      const uint32 queuedChunks = retransmissionQ->payloadQueue.size();
+      sctpEV3 << "Number of chunks in retransmissionQ: " << queuedChunks
+              <<" highestGapStop: "  << sackGapList.getGapStop(GapList::GT_Any, numGaps-1)
+              <<" highestTsnAcked: " << state->highestTsnAcked << endl;
+
+      // ====== Handle reneging =============================================
+      // highest gapStop smaller than highestTsnAcked: there might have been reneging
+      if (tsnLt(sackGapList.getGapStop(GapList::GT_Any, numGaps-1), state->highestTsnAcked)) {
+         // Reneging, type 2:
+         // In a previous SACK, chunks up to highestTsnAcked have been acked.
+         // This SACK contains a last gap ack < highestTsnAcked
+         //    => rereg TSNs from last gap ack to highestTsnAcked
+         //    => new highestTsnAcked = last gap ack
+         uint32 i = state->highestTsnAcked;
+         while (i >= sackGapList.getGapStop(GapList::GT_Any, numGaps - 1) + 1) {
+            // ====== Looking up TSN in retransmission queue ================
             SCTPDataVariables* myChunk = retransmissionQ->getChunk(i);
             if(myChunk) {
-                if(chunkHasBeenAcked(myChunk)) {
-                    tsnWasReneged(myChunk,
-                                      0);
-                }
+               if (chunkHasBeenAcked(myChunk)) {
+                  sctpEV3 << "TSN " << i << " was found. It has been un-acked." << endl;
+                  tsnWasReneged(myChunk, path, 2);
+                  sctpEV3 << "highestTsnAcked now " << state->highestTsnAcked << endl;
+               }
+            }
+            else {
+               sctpEV3 << "TSN " << i << " not found in retransmissionQ" << endl;
             }
             i--;
-        }
-        state->highestTsnAcked = tsna;
-    }
-
-
-    // #######################################################################
-    // #### Processing of GapAcks                                                        ####
-    // #######################################################################
-
-    if ((numGaps > 0) && (!retransmissionQ->payloadQueue.empty()) ) {
-        sctpEV3 << "===== Handling GapAcks after CumTSNAck " << tsna << " =====" << endl;
-        sctpEV3 << "We got " << numGaps << " gap reports" << endl;
-
-        // We got fragment reports... check for newly acked chunks.
-        const uint32 queuedChunks = retransmissionQ->payloadQueue.size();
-        sctpEV3 << "Number of chunks in retransmissionQ: " << queuedChunks
-                  <<" highestGapStop: "  << sackChunk->getGapStop(numGaps-1)
-                  <<" highestTsnAcked: " << state->highestTsnAcked << endl;
-
-        // ====== Handle reneging =============================================
-        // highest gapStop smaller than highestTsnAcked: there might have been reneging
-        if (tsnLt(sackChunk->getGapStop(numGaps-1), state->highestTsnAcked)) {
-            // Reneging, type 2:
-            // In a previous SACK, chunks up to highestTsnAcked have been acked.
-            // This SACK contains a last gap ack < highestTsnAcked
-            //      => rereg TSNs from last gap ack to highestTsnAcked
-            //      => new highestTsnAcked = last gap ack
-            uint32 i = state->highestTsnAcked;
-            while (i >= sackChunk->getGapStop(numGaps - 1) + 1) {
-                // ====== Looking up TSN in retransmission queue ================
-                SCTPDataVariables* myChunk = retransmissionQ->getChunk(i);
-                if(myChunk) {
-                    if (chunkHasBeenAcked(myChunk)) {
-                        sctpEV3 << "TSN " << i << " was found. It has been un-acked." << endl;
-                        tsnWasReneged(myChunk,
-                                          2);
-                        sctpEV3 << "highestTsnAcked now " << state->highestTsnAcked << endl;
-                    }
-                }
-                else {
-                    sctpEV3 << "TSN " << i << " not found in retransmissionQ" << endl;
-                }
-                i--;
+         }
+         state->highestTsnAcked = sackGapList.getGapStop(GapList::GT_Any, numGaps - 1);
+      }
+
+      // ====== Looking for changes in the gap reports ======================
+      sctpEV3 << "Looking for changes in gap reports" << endl;
+      for (int32 key = 0;key < numGaps; key++) {
+         const uint32 lo = sackGapList.getGapStart(GapList::GT_Any, key);
+         const uint32 hi = sackGapList.getGapStop(GapList::GT_Any, key);
+
+
+         // ====== Iterate over TSNs in gap reports =========================
+         sctpEV3 << "Examine TSNs between " << lo << " and " << hi << endl;
+         for (uint32 pos = lo; pos <= hi; pos++) {
+
+            // ====== Newly acked chunk =====================================
+            if(chunkMap->isAcked(pos) == false) {
+               SCTPDataVariables* myChunk = chunkMap->getChunk(pos);
+               if (myChunk) {
+                  SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath();
+                  assert(myChunkLastPath != NULL);
+                  // T.D. 02.02.2010: This chunk has been acked newly.
+                  //                  Let's process this new acknowledgement!
+                  handleChunkReportedAsAcked(highestNewAck, rttEstimation, myChunk,
+                                             path /* i.e. the SACK path for RTT measurement! */,
+                                             sackGapList.tsnIsNonRevokable(myChunk->tsn));
+                  if(sackGapList.tsnIsNonRevokable(pos)) {
+                     assert(chunkMap->getChunk(pos) == NULL);
+                  }
+               }
+               else {
+               }
             }
-            state->highestTsnAcked = sackChunk->getGapStop(numGaps - 1);
-        }
-
-        // ====== Looking for changes in the gap reports ======================
-        sctpEV3 << "Looking for changes in gap reports" << endl;
-        for (int32 key = 0;key < numGaps; key++) {
-            const uint32 lo = sackChunk->getGapStart(key);
-            const uint32 hi = sackChunk->getGapStop(key);
-
-
-            // ====== Iterate over TSNs in gap reports =========================
-            sctpEV3 << "Examine TSNs between " << lo << " and " << hi << endl;
-            for (uint32 pos = lo; pos <= hi; pos++) {
-                SCTPDataVariables* myChunk = retransmissionQ->getChunkFast(pos, getChunkFastFirstTime);
-                if (myChunk) {
-                    if(chunkHasBeenAcked(myChunk) == false) {
-                        SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath();
-                        assert(myChunkLastPath != NULL);
-                        // T.D. 02.02.2010: This chunk has been acked newly.
-                        //                        Let's process this new acknowledgement!
-                        handleChunkReportedAsAcked(highestNewAck, rttEstimation, myChunk,
-                                                            path /* i.e. the SACK path for RTT measurement! */);
-                    }
-                }
+
+            // ====== R-acked chunk became NR-acked =========================
+            else if(sackGapList.tsnIsNonRevokable(pos)) {   // T.D. 02.11.2010
+               SCTPDataVariables* myChunk = chunkMap->getChunk(pos);
+               if(myChunk) {
+                  // myChunk != NULL -> R-acked before, but not NR-acked
+                  handleChunkReportedAsAcked(highestNewAck, rttEstimation, myChunk,
+                                             path /* i.e. the SACK path for RTT measurement! */,
+                                             sackGapList.tsnIsNonRevokable(myChunk->tsn));
+                  assert(chunkMap->getChunk(pos) == NULL);
+                  // All NR-acked chunks have chunkMap->getChunk(pos) == NULL!
+               }
             }
-        }
-        state->highestTsnAcked = sackChunk->getGapStop(numGaps-1);
-
-        // ====== Examine chunks between the gap reports ======================
-        // They might have to be retransmitted or they could have been removed
-        uint32 lo = tsna;
-        for (int32 key = 0; key < numGaps; key++) {
-            const uint32 hi = sackChunk->getGapStart(key);
-            for (uint32 pos = lo+1; pos <= hi - 1; pos++) {
-                SCTPDataVariables* myChunk = retransmissionQ->getChunkFast(pos, getChunkFastFirstTime);
-                if(myChunk) {
-                    handleChunkReportedAsMissing(sackChunk, highestNewAck, myChunk,
-                                                          path /* i.e. the SACK path for RTT measurement! */);
-                }
-                else {
-                    sctpEV3 << "TSN " << pos << " not found in retransmissionQ" << endl;
-                }
+         }
+      }
+      state->highestTsnAcked = sackGapList.getGapStop(GapList::GT_Any, numGaps-1);
+
+      // ====== Examine chunks between the gap reports ======================
+      // They might have to be retransmitted or they could have been removed
+      uint32 lo = tsna;
+      for (int32 key = 0; key < numGaps; key++) {
+         const uint32 hi = sackGapList.getGapStart(GapList::GT_Any, key);
+         for (uint32 pos = lo+1; pos <= hi - 1; pos++) {
+            SCTPDataVariables* myChunk = chunkMap->getChunk(pos);
+            if(myChunk) {
+               handleChunkReportedAsMissing(sackChunk, highestNewAck, myChunk,
+                                            path /* i.e. the SACK path for RTT measurement! */);
             }
-            lo = sackChunk->getGapStop(key);
-        }
-
-
-        // ====== Validity checks =============================================
-    }
-
-
-    // ====== Update Fast Recovery status, according to SACK =================
-    updateFastRecoveryStatus(state->lastTsnAck);
-
-    // ====== Update RTT measurement for newly acked data chunks =============
-    sctpEV3 << simTime() << ": SACK: rtt=" << rttEstimation
-              << ", path=" << path->remoteAddress << endl;
-    pmRttMeasurement(path, rttEstimation);
-
-
-
-    // #######################################################################
-    // #### Receiver Window Management                                               ####
-    // #######################################################################
-
-    const uint32 osb = getOutstandingBytes();
-    state->peerRwnd = arwnd - osb;
-
-    // position of statement changed 20.07.05 I.R.
-    if ((int32)(state->peerRwnd) < 0) {
-        state->peerRwnd = 0;
-    }
-    if (state->peerRwnd > state->initialPeerRwnd) {
-        state->peerRwnd = state->initialPeerRwnd;
-    }
-    if (arwnd == 1 || state->peerRwnd < state->swsLimit || arwnd == 0) {
-        sctpEV3 << "processSackArrived: arwnd=" << arwnd
-                  << " state->peerRwnd=" << state->peerRwnd
-                  << " set peerWindowFull" << endl;
-        state->peerWindowFull = true;
-    }
-    else
-    {
-        state->peerWindowFull    = false;
-        state->zeroWindowProbing = false;
-    }
-#ifdef PVIATE
-    advancePeerTsn();
-#endif
+            else {
+               sctpEV3 << "TSN " << pos << " not found in retransmissionQ" << endl;
+            }
+         }
+         lo = sackGapList.getGapStop(GapList::GT_Any, key);
+      }
 
-    // ====== Need for zero-window probing? ==================================
-    if(osb == 0) {
-        if (arwnd == 0)
-            state->zeroWindowProbing = true;
-    }
 
+      // ====== Validity checks =============================================
+      if(SCTP::checkQueues) {
+         assert(state->highestTsnAcked == sackGapList.getGapStop(GapList::GT_Any, numGaps - 1));
+      }
+      path->vectorPathAckedTSNGapAck->record(state->highestTsnAcked);
+   }
+
+
+   // ====== Buffer space may have been gained => tell application ==========
+   if(sendBufferBeforeUpdate != state->sendBuffer) {
+      generateSendQueueAbatedIndication(sendBufferBeforeUpdate - state->sendBuffer);
+   }
+
+   // ====== Update Fast Recovery status, according to SACK =================
+   updateFastRecoveryStatus(state->lastTsnAck);
+
+   // ====== Update RTT measurement for newly acked data chunks =============
+   sctpEV3 << simTime() << ": SACK: rtt=" << rttEstimation
+           << ", path=" << path->remoteAddress << endl;
+   pmRttMeasurement(path, rttEstimation);
+
+   // ====== Record statistics ==============================================
+   for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
+      SCTPPathVariables* myPath = piter->second;
+      myPath->statisticsPathGapAckedChunksInLastSACK->record(myPath->gapAckedChunksInLastSACK);
+      myPath->statisticsPathGapNRAckedChunksInLastSACK->record(myPath->gapNRAckedChunksInLastSACK);
+      myPath->statisticsPathGapUnackedChunksInLastSACK->record(myPath->gapUnackedChunksInLastSACK);
+   }
+
+
+   // #######################################################################
+   // #### Receiver Window Management                                    ####
+   // #######################################################################
+
+   const uint32 osb = getOutstandingBytes();
+   if (state->bytesToAddPerPeerChunk > 0) {
+      state->peerRwnd = arwnd - osb  - (state->outstandingMessages * state->bytesToAddPerPeerChunk);
+   }
+   else if (state->peerAllowsChunks) {
+      state->peerMsgRwnd = msgRwnd - state->outstandingMessages;
+      state->peerRwnd    = arwnd - osb;
+      if ((int32)(state->peerMsgRwnd)< 0) {
+         state->peerMsgRwnd = 0;
+      }
+      if (state->peerMsgRwnd > state->initialPeerMsgRwnd) {
+         state->peerMsgRwnd = state->initialPeerMsgRwnd;
+      }
+   }
+   else {
+      state->peerRwnd = arwnd - osb;
+   }
+
+   // position of statement changed 20.07.05 I.R.
+   if ((int32)(state->peerRwnd) < 0) {
+      state->peerRwnd = 0;
+   }
+   if (state->peerRwnd > state->initialPeerRwnd) {
+      state->peerRwnd = state->initialPeerRwnd;
+   }
+   if (state->peerAllowsChunks && msgRwnd == 0) {
+      state->peerWindowFull = true;
+   }
+   if (arwnd == 1 || state->peerRwnd < state->swsLimit || arwnd == 0) {
+      sctpEV3 << "processSackArrived: arwnd=" << arwnd
+              << " state->peerRwnd=" << state->peerRwnd
+              << " set peerWindowFull" << endl;
+      state->peerWindowFull = true;
+   }
+   else if ((state->peerAllowsChunks && msgRwnd > 0) || !state->peerAllowsChunks) {
+      state->peerWindowFull    = false;
+      state->zeroWindowProbing = false;
+   }
+   advancePeerTsn();
+   statisticsArwndInLastSACK->record(arwnd);
+   statisticsPeerRwnd->record(state->peerRwnd);
+
+   // ====== Need for zero-window probing? ==================================
+   if(osb == 0) {
+      if ((state->peerAllowsChunks && msgRwnd == 0) || arwnd == 0) {
+         state->zeroWindowProbing = true;
+      }
+   }
 
-    // #######################################################################
-    // #### Congestion Window Management                                             ####
-    // #######################################################################
 
-    sctpEV3 << "Before ccUpdateBytesAcked: ";
-    for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
-        SCTPPathVariables* myPath    = piter->second;
-        const IPvXAddress& myPathId = myPath->remoteAddress;
+   // #######################################################################
+   // #### Congestion Window Management                                  ####
+   // #######################################################################
 
+   // ====== Update congestion windows on paths =============================
+   sctpEV3 << "Before ccUpdateBeforeSack with tsna=" << tsna << endl;
+   // ccUpdateBeforeSack() will iterate over all paths.
+   (this->*ccFunctions.ccUpdateBeforeSack)();
 
-        if(myPath->newlyAckedBytes > 0) {
-            // T.D. 07.10.2009: Only call ccUpdateBytesAcked() when there are
-            //                        acked bytes on this path!
-            const bool advanceWindow = myPath->newCumAck;
+   // ======= Update congestion window of each path =========================
+   sctpEV3 << "Before ccUpdateBytesAcked: ";
+   if(SCTP::checkQueues) {
+      dumpPaths();
+   }
+   for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
+      SCTPPathVariables* myPath   = piter->second;
+      const IPvXAddress& myPathId = myPath->remoteAddress;
 
-            sctpEV3 << simTime() << ":\tCC " << myPath->newlyAckedBytes
-                      << " newly acked on path " << myPathId << ";"
-                      << "\t->\tadvanceWindow="       << advanceWindow << endl;
 
-            (this->*ccFunctions.ccUpdateBytesAcked)(myPath, myPath->newlyAckedBytes, advanceWindow);
-        }
-    }
+      if(myPath->newlyAckedBytes > 0) {
+         // T.D. 07.10.2009: Only call ccUpdateBytesAcked() when there are
+         //                  acked bytes on this path!
+         const bool advanceWindow = myPath->newCumAck;
 
-    // ====== Update congestion windows on paths (handling decreases) ========
-    sctpEV3 << "Before ccUpdateAfterSack with tsna=" << tsna << endl;
-    // ccUpdateAfterSack() will iterate over all paths.
-    (this->*ccFunctions.ccUpdateAfterSack)();
+         sctpEV3 << simTime() << ":\tCC " << myPath->newlyAckedBytes
+                 << " newly acked on path " << myPathId << ";"
+                 << "\tdropFilledGap="            << ((dropFilledGap == true) ? "true" : "false")
+                 << "\t->\tadvanceWindow="        << advanceWindow << endl;
 
+         (this->*ccFunctions.ccUpdateBytesAcked)(myPath, myPath->newlyAckedBytes,
+                                                 (advanceWindow && dropFilledGap) ? false :
+                                                                                    advanceWindow);
+      }
+   }
 
-    // #######################################################################
-    // #### Path Management                                                              ####
-    // #######################################################################
 
-    // ====== Need to stop or restart T3 timer? ==============================
-    for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
-        SCTPPathVariables* myPath    = piter->second;
-        const IPvXAddress& myPathId = myPath->remoteAddress;
+   // ====== Update congestion windows on paths (handling decreases) ========
+   sctpEV3 << "Before ccUpdateAfterSack with tsna=" << tsna << endl;
+   // ccUpdateAfterSack() will iterate over all paths.
+   (this->*ccFunctions.ccUpdateAfterSack)();
 
-        if (myPath->outstandingBytes == 0) {
-            // T.D. 07.01.2010: Only stop T3 timer when there is nothing more to send on this path!
-            if(qCounter.roomTransQ.find(myPath->remoteAddress)->second == 0) {
-                // Stop T3 timer, if there are no more outstanding bytes.
-                stopTimer(myPath->T3_RtxTimer);
-            }
-        }
-        else if (myPath->newCumAck) {
+
+   // #######################################################################
+   // #### Path Management                                               ####
+   // #######################################################################
+
+
+   // ====== Need to stop or restart T3 timer? ==============================
+   for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) {
+      SCTPPathVariables* myPath   = piter->second;
+      const IPvXAddress& myPathId = myPath->remoteAddress;
+
+
+      if (myPath->outstandingBytes == 0) {
+         // T.D. 07.01.2010: Only stop T3 timer when there is nothing more to send on this path!
+         if(qCounter.roomTransQ.find(myPath->remoteAddress)->second == 0) {
+            // Stop T3 timer, if there are no more outstanding bytes.
+            stopTimer(myPath->T3_RtxTimer);
+         }
+      }
+      else if (myPath->newCumAck) {
+         stopTimer(myPath->T3_RtxTimer);
+         startTimer(myPath->T3_RtxTimer, myPath->pathRto);
+      }
+      else {
+         /* Also restart T3 timer, when lowest TSN is rtx'ed */
+         if(myPath->lowestTSNRetransmitted == true) {
+            sctpEV3 << "Lowest TSN retransmitted => restart of T3 timer for path "
+                     << myPathId << endl;
             stopTimer(myPath->T3_RtxTimer);
             startTimer(myPath->T3_RtxTimer, myPath->pathRto);
-        }
-        else {
-            /* Also restart T3 timer, when lowest TSN is rtx'ed */
-            if(myPath->lowestTSNRetransmitted == true) {
-                sctpEV3 << "Lowest TSN retransmitted => restart of T3 timer for path "
-                            << myPathId << endl;
-                stopTimer(myPath->T3_RtxTimer);
-                startTimer(myPath->T3_RtxTimer, myPath->pathRto);
-            }
-        }
-
-        // ====== Clear error counter if TSNs on path have been acked =========
-        if (myPath->newlyAckedBytes > 0) {
-            pmClearPathCounter(myPath);
-        }
-    }
+         }
+      }
 
+      // ====== Clear error counter if TSNs on path have been acked =========
+      if (myPath->newlyAckedBytes > 0) {
+         pmClearPathCounter(myPath);
+      }
+   }
 
+   sctpEV3 << "After processSackArrived for path " << path->remoteAddress
+           << " with tsna=" << tsna << ":" << endl;
+   if(SCTP::checkQueues) {
+      dumpPaths();
+      checkOutstandingBytes();
+   }
 
-    return SCTP_E_IGNORE;
+   return SCTP_E_IGNORE;
 }
 
 
-void SCTPAssociation::handleChunkReportedAsAcked(uint32&                  highestNewAck,
-                                                                 simtime_t&           rttEstimation,
-                                                                 SCTPDataVariables* myChunk,
-                                                                 SCTPPathVariables* sackPath)
+void SCTPAssociation::handleChunkReportedAsAcked(uint32&            highestNewAck,
+                                                 simtime_t&         rttEstimation,
+                                                 SCTPDataVariables* myChunk,
+                                                 SCTPPathVariables* sackPath,
+                                                 const bool         sackIsNonRevokable)
 {
-    SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath();
-    if ( (myChunk->numberOfTransmissions == 1) &&
-          (myChunk->hasBeenReneged == false) ) {
-        if (myChunkLastPath == sackPath) {
-            const simtime_t timeDifference = simTime() - myChunk->sendTime;
-            if((timeDifference < rttEstimation) || (rttEstimation == -1.0)) {
-                rttEstimation = timeDifference;
-            }
-            sctpEV3 << simTime()          << " processSackArrived: computed rtt time diff == "
-                      << timeDifference << " for TSN "<< myChunk->tsn << endl;
-        }
-    }
-    if ( (myChunk->hasBeenAbandoned == false)    &&
-          (myChunk->hasBeenReneged == false) ) {
-        myChunkLastPath->newlyAckedBytes += (myChunk->booksize);
-
-        sctpEV3 << simTime() << ": GapAcked TSN " << myChunk->tsn
-                  << " on path " << myChunkLastPath->remoteAddress << endl;
-
-        if (myChunk->tsn > highestNewAck) {
-            highestNewAck = myChunk->tsn;
-        }
-        ackChunk(myChunk);
-        if (myChunk->countsAsOutstanding) {
-            decreaseOutstandingBytes(myChunk);
-        }
-        if (transmissionQ->getChunk(myChunk->tsn)) {      // I.R. 02.01.07
-            sctpEV3 << "Found TSN " << myChunk->tsn << " in transmissionQ -> remote it" << endl;
-            transmissionQ->removeMsg(myChunk->tsn);
-            myChunk->enqueuedInTransmissionQ = false;
-            CounterMap::iterator q = qCounter.roomTransQ.find(myChunk->getNextDestination());
-            q->second -= ADD_PADDING(myChunk->len/8+SCTP_DATA_CHUNK_LENGTH);
-            CounterMap::iterator qb = qCounter.bookedTransQ.find(myChunk->getNextDestination());
-            qb->second -= myChunk->booksize;
-        }
-        myChunk->gapReports = 0;
-    }
+   SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath();
+   if ( (myChunk->numberOfTransmissions == 1) &&
+        (myChunk->hasBeenMoved == false) &&
+        (myChunk->hasBeenReneged == false) ) {
+      if (myChunkLastPath == sackPath) {
+         const simtime_t timeDifference = simTime() - myChunk->sendTime;
+         if((timeDifference < rttEstimation) || (rttEstimation == -1.0)) {
+            rttEstimation = timeDifference;
+         }
+         sctpEV3 << simTime()      << " processSackArrived: computed rtt time diff == "
+                 << timeDifference << " for TSN "<< myChunk->tsn << endl;
+      }
+   }
+
+   if( (myChunk->hasBeenAcked == false) &&
+       (myChunk->hasBeenReneged == false) &&
+       (myChunk->hasBeenAbandoned == false) ) {
+      sctpEV3 << simTime() << ": GapAcked TSN " << myChunk->tsn
+              << " on path " << myChunkLastPath->remoteAddress << endl;
+
+      if (myChunk->tsn > highestNewAck) {
+         highestNewAck = myChunk->tsn;
+      }
+
+      recordAcknowledgement(myChunk, myChunkLastPath);
+      if(sackIsNonRevokable == true) {
+         myChunkLastPath->gapAckedChunksInLastSACK++;
+         myChunkLastPath->gapNRAckedChunksInLastSACK++;
+      }
+      else {
+         myChunkLastPath->gapAckedChunksInLastSACK++;
+      }
+   }
+
+   // ====== Non-Renegable SACK =============================================
+   if(sackIsNonRevokable == true) {
+      // NOTE: nonRenegablyAckChunk() will only work with ChunkMap,
+      //       since the actual chunk object will be gone ...
+      nonRenegablyAckChunk(myChunk, sackPath, rttEstimation,
+                           sctpMain->getAssocStat(assocId));
+   }
+
+   // ====== Renegable SACK =================================================
+   else {
+      renegablyAckChunk(myChunk, sackPath);
+   }
 }
 
 
-void SCTPAssociation::handleChunkReportedAsMissing(const SCTPSackChunk*      sackChunk,
-                                                                    const uint32                 highestNewAck,
-                                                                    SCTPDataVariables*       myChunk,
-                                                                    const SCTPPathVariables* sackPath)
+void SCTPAssociation::handleChunkReportedAsMissing(const SCTPSackChunk*     sackChunk,
+                                                   const uint32             highestNewAck,
+                                                   SCTPDataVariables*       myChunk,
+                                                   const SCTPPathVariables* sackPath)
 {
-    SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath();
-    sctpEV3 << "TSN " << myChunk->tsn << " is missing in gap reports (last "
-              << myChunkLastPath->remoteAddress << ") ";
-    if (!chunkHasBeenAcked(myChunk)) {
-        sctpEV3 << "has not been acked, highestNewAck=" << highestNewAck
-                  << " countsAsOutstanding=" << myChunk->countsAsOutstanding << endl;
-        const uint32 chunkReportedAsMissing = (highestNewAck > myChunk->tsn) ? 1 : 0;
-        if (chunkReportedAsMissing > 0) {
-            // T.D. 15.04.09: Increase gapReports by chunkReportedAsMissing.
-            // Fixed bug here: gapReports += chunkReportedAsMissing instead of gapReports = chunkReportedAsMissing.
-            /*
-            printf("GapReports for TSN %u [ret=%d,fast=%s] at t=%s:  %d --> %d by %d\n",
-                        myChunk->tsn,
-                        myChunk->numberOfRetransmissions,
-                        (myChunk->hasBeenFastRetransmitted == true) ? "YES" : "no",
-                        simTime().str().c_str(),
-                        myChunk->gapReports,
-                        myChunk->gapReports + chunkReportedAsMissing,
-                        chunkReportedAsMissing);
-            */
-            myChunk->gapReports += chunkReportedAsMissing;
-
-            myChunkLastPath->gapNAcksInLastSACK++;
-
-            if (myChunk->gapReports >= state->numGapReports) {
-                bool fastRtx = false;
-                fastRtx = ((myChunk->hasBeenFastRetransmitted == false) &&
-                              (myChunk->numberOfRetransmissions == 0));
-                if (fastRtx) {
-
-                    // ====== Add chunk to transmission queue ========
-                    SCTPQueue::PayloadQueue::iterator it = transmissionQ->payloadQueue.find(myChunk->tsn);
-                    if (transmissionQ->getChunk(myChunk->tsn) == NULL) {
-                        SCTP::AssocStat* assocStat = sctpMain->getAssocStat(assocId);
-                        if(assocStat) {
-                            assocStat->numFastRtx++;
-                        }
-                        myChunk->hasBeenFastRetransmitted = true;
-
-                        sctpEV3 << simTime() << ": Fast RTX for TSN "
-                                  << myChunk->tsn << " on path " << myChunk->getLastDestination() << endl;
-                        myChunkLastPath->numberOfFastRetransmissions++;
-
-                        myChunk->setNextDestination(getNextDestination(myChunk));
-                        SCTPPathVariables* myChunkNextPath = myChunk->getNextDestinationPath();
-                        assert(myChunkNextPath != NULL);
-
-                        if (myChunk->countsAsOutstanding) {
-                            decreaseOutstandingBytes(myChunk);
-                        }
-                        if (!transmissionQ->checkAndInsertChunk(myChunk->tsn, myChunk)) {
-                            sctpEV3 << "Fast RTX: cannot add message/chunk (TSN="
-                                      << myChunk->tsn << ") to the transmissionQ" << endl;
-                        }
-                        else    {
-                            myChunk->enqueuedInTransmissionQ = true;
-                            CounterMap::iterator q = qCounter.roomTransQ.find(myChunk->getNextDestination());
-                            q->second += ADD_PADDING(myChunk->len/8+SCTP_DATA_CHUNK_LENGTH);
-                            CounterMap::iterator qb = qCounter.bookedTransQ.find(myChunk->getNextDestination());
-                            qb->second += myChunk->booksize;
-                        }
-                        myChunkNextPath->requiresRtx = true;
-                        if(myChunkNextPath->findLowestTSN == true) {
-                            // TD 08.12.09: fixed detection of lowest TSN retransmitted
-                            myChunkNextPath->lowestTSNRetransmitted = true;
-                        }
-                    }
-                }
+   SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath();
+   sctpEV3 << "TSN " << myChunk->tsn << " is missing in gap reports (last "
+           << myChunkLastPath->remoteAddress << ") ";
+   if (!chunkHasBeenAcked(myChunk)) {
+      sctpEV3 << "has not been acked, highestNewAck=" << highestNewAck
+              << " countsAsOutstanding=" << myChunk->countsAsOutstanding << endl;
+      const uint32 chunkReportedAsMissing = (highestNewAck > myChunk->tsn) ? 1 : 0;
+      if (chunkReportedAsMissing > 0) {
+         // T.D. 15.04.09: Increase gapReports by chunkReportedAsMissing.
+         // Fixed bug here: gapReports += chunkReportedAsMissing instead of gapReports = chunkReportedAsMissing.
+         /*
+         printf("GapReports for TSN %u [ret=%d,fast=%s] at t=%s:   %d --> %d by %d\n",
+                  myChunk->tsn,
+                  myChunk->numberOfRetransmissions,
+                  (myChunk->hasBeenFastRetransmitted == true) ? "YES" : "no",
+                  simTime().str().c_str(),
+                  myChunk->gapReports,
+                  myChunk->gapReports + chunkReportedAsMissing,
+                  chunkReportedAsMissing);
+         */
+         myChunk->gapReports += chunkReportedAsMissing;
+         myChunkLastPath->gapUnackedChunksInLastSACK++;
+
+         if (myChunk->gapReports >= state->numGapReports) {
+            bool fastRtx = false;
+            switch (state->rtxMethod) {
+               case 0: // Only one Fast RTX after 3 Gap reports
+                  fastRtx = ((myChunk->hasBeenFastRetransmitted == false) &&
+                             ((myChunk->numberOfRetransmissions == 0)
+                            ));
+                  break;
+               case 1: // Just 1 Fast RTX per RTT
+                  fastRtx = ((myChunk->hasBeenFastRetransmitted == false) &&
+                             (myChunk->numberOfRetransmissions == 0 ||
+                             (simTime() - myChunk->sendTime) > myChunkLastPath->srtt));
+                  break;
+               case 2: // Switch off Fast RTX
+                  fastRtx = false;
+                  break;
+               case 3: // Always Fast RTX
+                  fastRtx = true;
+                  break;
             }
-        }
-        else {
-            myChunk->hasBeenFastRetransmitted = false;
-            sctpEV3 << "TSN " << myChunk->tsn << " countsAsOutstanding="
-                        << myChunk->countsAsOutstanding << endl;
-            if (highestNewAck > myChunk->tsn) {
-                myChunk->gapReports++;
+            if (fastRtx) {
+
+               // ====== Add chunk to transmission queue ========
+               if (transmissionQ->getChunk(myChunk->tsn) == NULL) {
+                  SCTP::AssocStat* assocStat = sctpMain->getAssocStat(assocId);
+                  if(assocStat) {
+                     assocStat->numFastRtx++;
+                  }
+                  myChunk->hasBeenFastRetransmitted = true;
+                  myChunk->sendForwardIfAbandoned = true;
+
+#ifdef RCVMESSAGE_DEBUG
+                  std::cout << simTime() << ": Fast RTX for TSN "
+                            << myChunk->tsn << " on path " << myChunk->getLastDestination() << endl;
+#endif
+                  sctpEV3 << simTime() << ": Fast RTX for TSN "
+                          << myChunk->tsn << " on path " << myChunk->getLastDestination() << endl;
+                  myChunkLastPath->numberOfFastRetransmissions++;
+
+                  myChunk->setNextDestination(getNextDestination(myChunk));
+                  SCTPPathVariables* myChunkNextPath = myChunk->getNextDestinationPath();
+                  assert(myChunkNextPath != NULL);
+
+                  if (myChunk->countsAsOutstanding) {
+                     decreaseOutstandingBytes(myChunk);
+                  }
+                  if (!transmissionQ->checkAndInsertChunk(myChunk->tsn, myChunk)) {
+                     sctpEV3 << "Fast RTX: cannot add message/chunk (TSN="
+                             << myChunk->tsn << ") to the transmissionQ" << endl;
+                  }
+                  else  {
+                     myChunk->enqueuedInTransmissionQ = true;
+                     CounterMap::iterator q = qCounter.roomTransQ.find(myChunk->getNextDestination());
+                     q->second += ADD_PADDING(myChunk->len/8+SCTP_DATA_CHUNK_LENGTH);
+                     CounterMap::iterator qb = qCounter.bookedTransQ.find(myChunk->getNextDestination());
+                     qb->second += myChunk->booksize;
+                  }
+                  myChunkNextPath->requiresRtx = true;
+                  if(myChunkNextPath->findLowestTSN == true) {
+                     // TD 08.12.09: fixed detection of lowest TSN retransmitted
+                     myChunkNextPath->lowestTSNRetransmitted = true;
+                  }
+               }
             }
-            myChunkLastPath->gapAcksInLastSACK++;
-        }
-        myChunkLastPath->findLowestTSN = false;
-    }
-    else {
-        // Reneging, type 1:
-        // A chunk in the gap blocks has been un-acked => reneg it.
-                    tsnWasReneged(myChunk,
-                                      1);
-    }
+         }
+      }
+      myChunkLastPath->findLowestTSN = false;
+   }
+   else {
+      // Reneging, type 1:
+      // A chunk in the gap blocks has been un-acked => reneg it.
+      tsnWasReneged(myChunk, sackPath, 1);
+   }
 }
 
 
-uint32 SCTPAssociation::dequeueAckedChunks(const uint32       tsna,
-                                                         SCTPPathVariables* sackPath,
-                                                         simtime_t&           rttEstimation)
+void SCTPAssociation::nonRenegablyAckChunk(SCTPDataVariables* chunk,
+                                           SCTPPathVariables* sackPath,
+                                           simtime_t&         rttEstimation,
+                                           SCTP::AssocStat*   assocStat)
 {
-    SCTP::AssocStat* assocStat                   = sctpMain->getAssocStat(assocId);
-    uint32            newlyAckedBytes            = 0;
-    uint64            sendBufferBeforeUpdate = state->sendBuffer;
-
-    // Set it ridiculously high
-    rttEstimation = MAXTIME;
-
-    // Are there chunks in the retransmission queue ? If Yes -> check for dequeue.
-    SCTPQueue::PayloadQueue::iterator iterator = retransmissionQ->payloadQueue.begin();
-    while (iterator != retransmissionQ->payloadQueue.end()) {
-        SCTPDataVariables* chunk = iterator->second;
-        if (tsnGe(tsna, chunk->tsn)) {
-            // Dequeue chunk, cause it has been acked
-            if (transmissionQ->getChunk(chunk->tsn)) {
-                transmissionQ->removeMsg(chunk->tsn);
-                chunk->enqueuedInTransmissionQ = false;
-                CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination());
-                q->second -= ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH);
-                CounterMap::iterator qb = qCounter.bookedTransQ.find(chunk->getNextDestination());
-                qb->second -= chunk->booksize;
-            }
+   SCTPPathVariables* lastPath = chunk->getLastDestinationPath();
+   assert(lastPath != NULL);
+
+   // ====== Bookkeeping ====================================================
+   // Subtract chunk size from sender buffer size
+   state->sendBuffer -= chunk->len/8;
+
+   // Subtract chunk size from the queue size of its stream
+   SCTPSendStreamMap::iterator streamIterator = sendStreams.find(chunk->sid);
+   assert(streamIterator != sendStreams.end());
+   SCTPSendStream* stream = streamIterator->second;
+   assert(stream != NULL);
+   SCTPDataMsgQueue* streamQ = (chunk->ordered == false) ? stream->getUnorderedStreamQ() : stream->getStreamQ();
+   assert(streamQ != NULL);
+   streamQ->subLen(chunk->len / 8);
+
+   if (chunk->priority > 0) {
+      state->queuedDroppableBytes -= chunk->len/8;
+   }
+
+   if ( (chunk->hasBeenCountedAsNewlyAcked == false) &&
+        (chunk->hasBeenAcked == false) ) {
+         chunk->hasBeenCountedAsNewlyAcked = true;
+         // T.D. 13.07.2011: The chunk has not been acked before.
+         //                  Therefore, its size may *once* be counted as newly acked.
+         lastPath->newlyAckedBytes += chunk->booksize;
+   }
+
+   assert(chunk->queuedOnPath->queuedBytes >= chunk->booksize);
+   chunk->queuedOnPath->queuedBytes -= chunk->booksize;
+   chunk->queuedOnPath->statisticsPathQueuedSentBytes->record(chunk->queuedOnPath->queuedBytes);
+   chunk->queuedOnPath = NULL;
+
+   assert(state->queuedSentBytes >= chunk->booksize);
+   state->queuedSentBytes -= chunk->booksize;
+   statisticsQueuedSentBytes->record(state->queuedSentBytes);
+   if(assocStat) {
+      assocStat->ackedBytes += chunk->len/8;
+   }
+   if( (assocStat) && (fairTimer)) {
+      assocStat->fairAckedBytes += chunk->len/8;
+   }
+
+
+   // ====== RTT calculation ================================================
+   if ((chunkHasBeenAcked(chunk) == false) && (chunk->countsAsOutstanding)) {
+      if ((chunk->numberOfTransmissions == 1) && (lastPath == sackPath) && (chunk->hasBeenMoved == false)) {
+         const simtime_t timeDifference = simTime() - chunk->sendTime;
+         if ((timeDifference < rttEstimation) || (rttEstimation == MAXTIME)) {
+            rttEstimation = timeDifference;
+         }
+      }
+      decreaseOutstandingBytes(chunk);
+   }
+
+   // ====== Remove chunk pointer from ChunkMap =============================
+   // The chunk still has to be remembered as acknowledged!
+   ackChunk(chunk, sackPath);
+   chunkMap->setChunk(chunk->tsn, NULL);
+
+   // ====== Remove chunk from transmission queue ===========================
+   // Dequeue chunk, cause it has been acked
+   if (transmissionQ->getChunk(chunk->tsn)) {
+      transmissionQ->removeMsg(chunk->tsn);
+      chunk->enqueuedInTransmissionQ = false;
+      CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination());
+      q->second -= ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH);
+      CounterMap::iterator qb = qCounter.bookedTransQ.find(chunk->getNextDestination());
+      qb->second -= chunk->booksize;
+   }
+
+   // ====== Remove chunk from retransmission queue =========================
+   chunk = retransmissionQ->getAndExtractChunk(chunk->tsn);
+   if (chunk->userData != NULL) {
+      delete chunk->userData;
+   }
+   delete chunk;
+}
 
-            chunk = retransmissionQ->getAndExtractChunk(chunk->tsn);
-            state->sendBuffer -= chunk->len/8;
 
-            SCTPPathVariables* lastPath = chunk->getLastDestinationPath();
-            assert(lastPath != NULL);
+void SCTPAssociation::renegablyAckChunk(SCTPDataVariables* chunk,
+                                        SCTPPathVariables* sackPath)
+{
+   // ====== Bookkeeping ====================================================
+   if (chunk->countsAsOutstanding) {
+      decreaseOutstandingBytes(chunk);
+   }
+
+   if ( (chunk->hasBeenCountedAsNewlyAcked == false) &&
+        (chunk->hasBeenAcked == false) ) {
+         chunk->hasBeenCountedAsNewlyAcked = true;
+         // T.D. 13.07.2011: The chunk has not been acked before.
+         //                  Therefore, its size may *once* be counted as newly acked.
+         chunk->getLastDestinationPath()->newlyAckedBytes += chunk->booksize;
+   }
+
+    // ====== Acknowledge chunk =============================================
+   ackChunk(chunk, sackPath);
+   chunk->gapReports = 0;
+
+   // ====== Remove chunk from transmission queue ===========================
+   if (transmissionQ->getChunk(chunk->tsn)) {   // I.R. 02.01.2007
+      sctpEV3 << "Found TSN " << chunk->tsn << " in transmissionQ -> remote it" << endl;
+      transmissionQ->removeMsg(chunk->tsn);
+      chunk->enqueuedInTransmissionQ = false;
+      CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination());
+      q->second -= ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH);
+      CounterMap::iterator qb = qCounter.bookedTransQ.find(chunk->getNextDestination());
+      qb->second -= chunk->booksize;
+   }
+}
 
-            if (!chunkHasBeenAcked(chunk)) {
-                newlyAckedBytes += (chunk->booksize);
 
-                sctpEV3 << simTime() << ": CumAcked TSN " << chunk->tsn
-                          << " on path " << chunk->getLastDestination() << endl;
+void SCTPAssociation::generateSendQueueAbatedIndication(const uint64 bytes)
+{
+   if(state->sendBuffer < state->sendQueueLimit) {
+      // T.D. 06.02.2010: Just send SCTP_I_SENDQUEUE_ABATED once, after all newly acked
+      //                  chunks have been dequeued.
+      // I.R. only send indication if the sendBuffer size has dropped below the sendQueueLimit
+      // assert(state->lastSendQueueAbated < simTime());
+      state->appSendAllowed = true;
+      sctpEV3 << simTime() << ":\tSCTP_I_SENDQUEUE_ABATED("
+              << bytes << ") to refill buffer "
+              << state->sendBuffer << "/" << state->sendQueueLimit << endl;
+
+      cPacket* msg = new cPacket(indicationName(SCTP_I_SENDQUEUE_ABATED));
+      msg->setKind(SCTP_I_SENDQUEUE_ABATED);
+
+      SCTPSendQueueAbated* sendQueueAbatedIndication =
+         new SCTPSendQueueAbated(indicationName(SCTP_I_SENDQUEUE_ABATED));
+      sendQueueAbatedIndication->setAssocId(assocId);
+      sendQueueAbatedIndication->setLocalAddr(localAddr);
+      sendQueueAbatedIndication->setRemoteAddr(remoteAddr);
+      sendQueueAbatedIndication->setNumMsgs(bytes);   // NOTE: Legacy API!
+
+      sendQueueAbatedIndication->setQueuedForStreamArraySize(sendStreams.size());
+      unsigned int streamID    = 0;
+      unsigned int totalQueued = 0;
+      for (SCTPSendStreamMap::iterator iterator = sendStreams.begin();
+           iterator != sendStreams.end(); ++iterator) {
+         const SCTPSendStream* stream = iterator->second;
+         totalQueued += stream->getQueuedBytes();
+         sendQueueAbatedIndication->setQueuedForStream(streamID, stream->getQueuedBytes());
+         streamID++;
+      }
+      assert(totalQueued == state->sendBuffer);   // Ensure that bookkeeping is okay!
 
-                lastPath->newlyAckedBytes += (chunk->booksize);
+      sendQueueAbatedIndication->setBytesAvailable(state->sendQueueLimit - state->sendBuffer);
+      sendQueueAbatedIndication->setBytesQueued(state->sendBuffer);
+      sendQueueAbatedIndication->setBytesLimit(state->sendQueueLimit);
 
-                // T.D. 05.12.09: CumAck affects lastPath -> reset its T3 timer later.
-                lastPath->newCumAck = true;
-            }
+      msg->setControlInfo(sendQueueAbatedIndication);
+      sctpMain->send(msg, "to_appl", appGateIndex);
+
+      state->lastSendQueueAbated = simTime();
+   }
+}
+
+
+void SCTPAssociation::dequeueAckedChunks(const uint32       tsna,
+                                           SCTPPathVariables* sackPath,
+                                           simtime_t&         rttEstimation)
+{
+   SCTP::AssocStat* assocStat = sctpMain->getAssocStat(assocId);
+
+   // Set it ridiculously high
+   rttEstimation = MAXTIME;
+
+   // Are there chunks in the retransmission queue? If Yes -> check for dequeue.
+   SCTPQueue::PayloadQueue::iterator iterator = retransmissionQ->payloadQueue.begin();
+   while (iterator != retransmissionQ->payloadQueue.end()) {
+      SCTPDataVariables* chunk = iterator->second;
+      if (tsnGe(tsna, chunk->tsn)) {
+         sctpEV3 << simTime() << ": CumAcked TSN " << chunk->tsn
+                 << " on path " << chunk->getLastDestination() << endl;
 
+         if (!chunkHasBeenAcked(chunk)) {
+            SCTPPathVariables* lastPath = chunk->getLastDestinationPath();
+            // T.D. 05.12.09: CumAck affects lastPath -> reset its T3 timer later.
+            lastPath->newCumAck = true;
+         }
+         recordDequeuing(chunk);
+         nonRenegablyAckChunk(chunk, sackPath, rttEstimation, assocStat);
+      }
+      else {
+         break;
+      }
+      iterator = retransmissionQ->payloadQueue.begin();
+   }
+
+   sctpEV3 << "dequeueAckedChunks(): rttEstimation=" << rttEstimation << endl;
+}
 
-            if(assocStat) {
-                assocStat->ackedBytes += chunk->len/8;
+
+SCTPEventCode SCTPAssociation::processForwardTsnArrived(SCTPForwardTsnChunk* fwChunk)
+{
+   sctpEV3<<"processForwardTsnArrived\n";
+   sctpEV3<<"last state->cTsnAck="<<state->gapList.getCumAckTSN()<<" arraySize="<<fwChunk->getSidArraySize()<<"\n";
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+   /* Ignore old FORWARD_TSNs, probably stale retransmits. */
+   if (state->gapList.getCumAckTSN() >= fwChunk->getNewCumTsn()) {
+      return SCTP_E_IGNORE;
+   }
+
+   for (uint32 i = 0; i < fwChunk->getSidArraySize(); i++) {
+      if (fwChunk->getSsn(i) != -1) {
+         SCTPReceiveStreamMap::iterator iter=receiveStreams.find(fwChunk->getSid(i));
+         SCTPReceiveStream* rStream = iter->second;
+
+         /* Uncomment the folloing to drop gap-acknowledged messages
+          * between two abandonend messages rather then delivering them.
+          */
+         rStream->setExpectedStreamSeqNum(fwChunk->getSsn(i)+1);
+         if (rStream->getExpectedStreamSeqNum() > 65535) {
+            rStream->setExpectedStreamSeqNum(0);
+         }
+         while (rStream->getOrderedQ()->getQueueSize() > 0) {
+            /* dequeue first from orderedQ */
+            sctpEV3 << "expectedSSn=" << rStream->getExpectedStreamSeqNum() << endl;
+            SCTPDataVariables* chunk =
+               rStream->getOrderedQ()-> dequeueChunkBySSN(rStream->getExpectedStreamSeqNum());
+            if (chunk) {
+               sctpEV3 << "insert from orderedQ in deliveryQ tsn="
+                       << chunk->tsn << endl;
+               qCounter.roomSumRcvStreams -= ADD_PADDING(chunk->len/8 + SCTP_DATA_CHUNK_LENGTH);
+               if (rStream->getDeliveryQ()->checkAndInsertChunk(chunk->tsn, chunk)) {
+                  sctpEV3 << "insert from orderedQ in deliveryQ tsn="
+                          << chunk->tsn << endl;
+                  int32 seqnum = rStream->getExpectedStreamSeqNum();
+                  rStream->setExpectedStreamSeqNum(++seqnum);
+                  if (rStream->getExpectedStreamSeqNum() > 65535) {
+                     rStream->setExpectedStreamSeqNum(0);
+                  }
+                  qCounter.roomSumRcvStreams += ADD_PADDING(chunk->len/8 + SCTP_DATA_CHUNK_LENGTH);
+               }
             }
+            else {
+               break;
+         }
+         }
+      }
+   }
+   /* Update Gap lists with abandoned TSNs and advance CumTSNAck */
+   for (uint32 i = state->gapList.getCumAckTSN() + 1; i <= fwChunk->getNewCumTsn(); i++) {
+      if (i > state->gapList.getCumAckTSN() && !state->gapList.tsnInGapList(i)) {
+         bool dummy;
+         state->gapList.updateGapList(i, dummy,
+                                      (state->disableReneging == false) ? true : false);
+         state->gapList.tryToAdvanceCumAckTSN();
+      }
+   }
 
-            if ((chunkHasBeenAcked(chunk) == false) && (chunk->countsAsOutstanding)) {
-                ackChunk(chunk);
-                if ((chunk->numberOfTransmissions == 1) && (chunk->getLastDestinationPath() == sackPath)) {
-                    const simtime_t timeDifference = simTime() - chunk->sendTime;
-                    if ((timeDifference < rttEstimation) || (rttEstimation == MAXTIME)) {
-                        rttEstimation = timeDifference;
-                    }
-                }
-                decreaseOutstandingBytes(chunk);
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+   return SCTP_E_IGNORE;
+}
+
+SCTPEventCode SCTPAssociation::processDataArrived(SCTPDataChunk* dataChunk)
+{
+   const uint32       tsn  = dataChunk->getTsn();
+   SCTPPathVariables* path = getPath(remoteAddr);
+
+   state->newChunkReceived      = false;
+   state->lastTsnReceived       = tsn;
+
+   bool found = false;
+   for(std::list<SCTPPathVariables*>::iterator iterator = state->lastDataSourceList.begin();
+       iterator != state->lastDataSourceList.end(); iterator++) {
+      if(*iterator == path) {
+         found = true;
+         break;
+      }
+   }
+   if(!found) {
+      state->lastDataSourceList.push_back(path);
+   }
+   state->lastDataSourcePath = path;
+
+   sctpEV3 << simTime() << " SCTPAssociation::processDataArrived TSN=" << tsn << endl;
+   path->vectorPathReceivedTSN->record(tsn);
+   if (dataChunk->getIBit()) {
+      state->ackState = sackFrequency;
+   }
+   calculateRcvBuffer();
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+
+   SCTPSimpleMessage* smsg = check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate());
+   dataChunk->setBitLength(SCTP_DATA_CHUNK_LENGTH*8);
+   dataChunk->encapsulate(smsg);
+   const uint32 payloadLength = dataChunk->getBitLength()/8-SCTP_DATA_CHUNK_LENGTH;
+   sctpEV3 << "state->bytesRcvd=" << state->bytesRcvd << endl;
+   state->bytesRcvd += payloadLength;
+   sctpEV3 << "state->bytesRcvd now=" << state->bytesRcvd << endl;
+   SCTP::AssocStatMap::iterator iter = sctpMain->assocStatMap.find(assocId);
+   iter->second.rcvdBytes += dataChunk->getBitLength()/8 - SCTP_DATA_CHUNK_LENGTH;
+   if (state->stopReceiving) {
+      return SCTP_E_IGNORE;
+   }
+
+   // ====== Duplicate: tsn < CumAckTSN =====================================
+   if (tsnLe(tsn, state->gapList.getCumAckTSN())) {
+      if (state->stopOldData) {
+         if (tsnGe(tsn,state->peerTsnAfterReset)) {
+            state->stopOldData = false;
+         }
+         return SCTP_E_IGNORE;
+      }
+      else {
+#ifdef RCVMESSAGE_DEBUG
+         std::cout << simTime() << ": Duplicate TSN " << tsn << " (smaller than CumAck)" << endl;
+#endif
+         sctpEV3 << simTime() << ": Duplicate TSN " << tsn << " (smaller than CumAck)" << endl;
+         state->dupList.push_back(tsn);
+         state->dupList.unique();
+         path->numberOfDuplicates++;
+         delete check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate());
+         return SCTP_E_DUP_RECEIVED;
+      }
+   }
+
+   // ====== Duplicate ======================================================
+   if (tsnIsDuplicate(tsn)) {
+      // TSN value is duplicate within a fragment
+      sctpEV3 << "Duplicate TSN " << tsn << " (copy)" << endl;
+      state->dupList.push_back(tsn);
+      state->dupList.unique();
+      path->numberOfDuplicates++;
+      return SCTP_E_IGNORE;
+   }
+
+   // ====== Out of receiver buffer space? ==================================
+   calculateRcvBuffer();
+   if ( ((state->messageAcceptLimit > 0) &&
+         (state->localMsgRwnd-state->bufferedMessages <= 0)) ||
+        ((state->messageAcceptLimit == 0) &&
+         ((int32)(state->localRwnd-state->queuedReceivedBytes -
+            state->bufferedMessages * state->bytesToAddPerRcvdChunk) <= 0)) ) {
+      state->ackState = sackFrequency;
+
+      if (tsnGt(tsn, state->gapList.getHighestTSNReceived())) {
+         printf("DROP: %d   high=%d   Q=%d  Rwnd=%d\n",   (int)tsn, (int)state->gapList.getHighestTSNReceived(), (int)state->queuedReceivedBytes,(int)state->localRwnd);
+// NUR ANNEHMEN, WENN CumAck DAMIT MÖGLICH ...
+//          if(state->disableReneging == false) {  ????
+
+            if ((!state->pktDropSent) && (sctpMain->pktdrop) && (state->peerPktDrop)) {
+               sctpEV3 << "Receive buffer full (case 1): sendPacketDrop" << endl;
+               sendPacketDrop(false);
             }
-            if (chunk->userData != NULL) {
-                delete chunk->userData;
+            iter->second.numDropsBecauseNewTSNGreaterThanHighestTSN++;
+            return SCTP_E_IGNORE;
+//          }  ????
+      }
+      else if ((tsn < state->gapList.getHighestTSNReceived()) &&
+               (state->disableReneging == false) &&
+               (!makeRoomForTsn(tsn, dataChunk->getBitLength()-SCTP_DATA_CHUNK_LENGTH*8, dataChunk->getUBit()))) {
+         if ((!state->pktDropSent) && (sctpMain->pktdrop) && (state->peerPktDrop)) {
+            sctpEV3 << "Receive buffer full (case 2): sendPacketDrop" << endl;
+            sendPacketDrop(false);
+         }
+         iter->second.numDropsBecauseNoRoomInBuffer++;
+         return SCTP_E_IGNORE;
+      }
+   }
+
+
+   // ====== Update of CumAckTSN ============================================
+   state->gapList.updateGapList(tsn, state->newChunkReceived,
+                                (state->disableReneging == false) ? true : false);
+   state->gapList.tryToAdvanceCumAckTSN();
+   sctpEV3 << "cumAckTSN=" << state->gapList.getCumAckTSN()
+           << " highestTSNReceived=" << state->gapList.getHighestTSNReceived() << endl;
+
+
+   // ====== Silly Window Syndrome Avoidance ================================
+   if (state->swsAvoidanceInvoked) {
+      // swsAvoidanceInvoked => schedule a SACK to be sent at once in this case
+      sctpEV3 << "swsAvoidanceInvoked" << endl;
+      state->ackState = sackFrequency;
+   }
+
+
+   // ====== Enqueue new chunk ==============================================
+   SCTPEventCode event = SCTP_E_SEND;
+   if (state->newChunkReceived) {
+      SCTPReceiveStreamMap::iterator iter = receiveStreams.find(dataChunk->getSid());
+      const int ret = iter->second->enqueueNewDataChunk(makeVarFromMsg(dataChunk));
+      if (ret > 0) {
+         state->queuedReceivedBytes += payloadLength;
+         calculateRcvBuffer();
+
+         event = SCTP_E_DELIVERED;
+         if (ret < 3) {
+            state->bufferedMessages++;
+            sendDataArrivedNotification(dataChunk->getSid());
+            putInDeliveryQ(dataChunk->getSid());
+            if (simTime() > state->lastThroughputTime + 1) {
+               for (uint16 i = 0; i < inboundStreams; i++) {
+                  streamThroughputVectors[i]->record(state->streamThroughput[i]  /
+                                                        (simTime() - state->lastThroughputTime) / 1024);
+                  state->streamThroughput[i] = 0;
+               }
+               state->lastThroughputTime = simTime();
             }
-            delete chunk;
-        }
-        else {
-            break;
-        }
-        iterator = retransmissionQ->payloadQueue.begin();
-    }
-
-    if(sendBufferBeforeUpdate != state->sendBuffer && state->sendBuffer < state->sendQueueLimit) {
-        // T.D. 06.02.2010: Just send SCTP_I_SENDQUEUE_ABATED once, after all newly acked
-        //                        chunks have been dequeued.
-        // I.R. only send indication if the sendBuffer size has dropped below the sendQueueLimit
-        assert(state->lastSendQueueAbated < simTime());
-        state->appSendAllowed = true;
-        sctpEV3 << simTime() << ":\tSCTP_I_SENDQUEUE_ABATED("
-                  << sendBufferBeforeUpdate - state->sendBuffer << ") to refill buffer "
-                  << state->sendBuffer << "/" << state->sendQueueLimit << endl;
-        sendIndicationToApp(SCTP_I_SENDQUEUE_ABATED, sendBufferBeforeUpdate - state->sendBuffer);
-        state->lastSendQueueAbated = simTime();
-    }
-
-    sctpEV3 << "dequeueAckedChunks(): newlyAckedBytes=" << newlyAckedBytes
-              << ", rttEstimation=" << rttEstimation << endl;
-
-    return (newlyAckedBytes);
+            state->streamThroughput[dataChunk->getSid()] += payloadLength;
+         }
+      calculateRcvBuffer();
+      }
+      state->newChunkReceived = false;
+   }
+
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+   return(event);
 }
 
 
+SCTPEventCode SCTPAssociation::processHeartbeatAckArrived(SCTPHeartbeatAckChunk* hback,
+                                                          SCTPPathVariables*     path)
+{
+   /* hb-ack goes to pathmanagement, reset error counters, stop timeout timer */
+   const IPvXAddress addr        = hback->getRemoteAddr();
+   const simtime_t   hbTimeField = hback->getTimeField();
+   stopTimer(path->HeartbeatTimer);
+   /* assume a valid RTT measurement on this path */
+   simtime_t rttEstimation = simTime() - hbTimeField;
+   pmRttMeasurement(path, rttEstimation);
+   pmClearPathCounter(path);
+   path->confirmed   = true;
+   path->lastAckTime = simTime();
+   if (path->primaryPathCandidate == true) {
+      state->setPrimaryPath(getPath(addr));
+      path->primaryPathCandidate = false;
+      if (path->pmtu < state->assocPmtu) {
+         state->assocPmtu = path->pmtu;
+      }
+      path->ssthresh = state->peerRwnd;
+      recordCwndUpdate(path);
+      path->heartbeatTimeout = (double)sctpMain->par("hbInterval") + path->pathRto;
+   }
+
+   if (path->activePath == false) {
+      sctpEV3 << "HB ACK arrived activePath=false. remoteAddress=" <<path->remoteAddress
+              << " initialPP=" << state->initialPrimaryPath << endl;
+      path->activePath = true;
+      if (state->reactivatePrimaryPath && path->remoteAddress == state->initialPrimaryPath) {
+         state->setPrimaryPath(path);
+      }
+      sctpEV3 << "primaryPath now " << state->getPrimaryPathIndex() << endl;
+   }
+   sctpEV3 << "Received HB ACK chunk...resetting error counters on path " << addr
+           <<", rttEstimation=" << rttEstimation << endl;
+   path->pathErrorCount = 0;
 
-SCTPEventCode SCTPAssociation::processDataArrived(SCTPDataChunk* dataChunk)
+   return SCTP_E_IGNORE;
+}
+
+void SCTPAssociation::processOutgoingResetRequestArrived(SCTPOutgoingSSNResetRequestParameter* requestParam)
 {
-    bool                     checkCtsnaChange = false;
-    const uint32         tsn                    = dataChunk->getTsn();
-    SCTPPathVariables* path                 = getPath(remoteAddr);
-
-    state->newChunkReceived       = false;
-    state->lastTsnReceived        = tsn;
-    state->lastDataSourceAddress = remoteAddr;
-
-    sctpEV3 << simTime() << " SCTPAssociation::processDataArrived TSN=" << tsn << endl;
-    path->pathRcvdTSN->record(tsn);
-
-    SCTPSimpleMessage* smsg = check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate());
-    dataChunk->setBitLength(SCTP_DATA_CHUNK_LENGTH*8);
-    dataChunk->encapsulate(smsg);
-    const uint32 payloadLength = dataChunk->getBitLength()/8-SCTP_DATA_CHUNK_LENGTH;
-    sctpEV3 << "state->bytesRcvd=" << state->bytesRcvd << endl;
-    state->bytesRcvd+=payloadLength;
-    sctpEV3 << "state->bytesRcvd now=" << state->bytesRcvd << endl;
-    SCTP::AssocStatMap::iterator iter=sctpMain->assocStatMap.find(assocId);
-    iter->second.rcvdBytes+=dataChunk->getBitLength()/8-SCTP_DATA_CHUNK_LENGTH;
-
-    if (state->numGaps == 0) {
-        state->highestTsnReceived = state->cTsnAck;
-    }
-    else {
-        state->highestTsnReceived = state->gapStopList[state->numGaps-1];
-    }
-    if (state->stopReceiving) {
-        return SCTP_E_IGNORE;
-    }
-
-    if (tsnLe(tsn, state->cTsnAck)) {
-            sctpEV3 << "Duplicate TSN " << tsn << " (smaller than CumAck)" << endl;
-            state->dupList.push_back(tsn);
-            state->dupList.unique();
-            delete check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate());
-            return SCTP_E_DUP_RECEIVED;
-    }
-
-
-    if ((int32)(state->localRwnd-state->queuedReceivedBytes) <= 0) {
-        if (tsnGt(tsn, state->highestTsnReceived)) {
-            return SCTP_E_IGNORE;
-        }
-        // changed 06.07.05 I.R.
-        else if ((!tsnIsDuplicate(tsn)) &&
-                    (tsn < state->highestTsnStored)) {
-            if (!makeRoomForTsn(tsn, dataChunk->getBitLength()-SCTP_DATA_CHUNK_LENGTH*8, dataChunk->getUBit())) {
-                delete check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate());
-                return SCTP_E_IGNORE;
+   sctpEV3<<"processOutgoingResetRequestArrived\n";
+   if (getPath(remoteAddr)->ResetTimer->isScheduled())
+   {
+      SCTPResetTimer* tm = check_and_cast<SCTPResetTimer*>(PK(getPath(remoteAddr)->ResetTimer)->decapsulate());
+      if (tm->getInSN() == requestParam->getSrResSn())
+      {
+         if (!tm->getInAcked() && tm->getOutAcked())
+         {
+            stopTimer(getPath(remoteAddr)->ResetTimer);
+            delete state->resetChunk;
+         }
+         else
+         {
+            tm->setInAcked(true);
+            PK(getPath(remoteAddr)->ResetTimer)->encapsulate(tm);
+         }
+      }
+
+   }
+   if (tsnGt(requestParam->getLastTsn(), state->gapList.getHighestTSNReceived())) {
+      state->lastTsnBeforeReset = requestParam->getLastTsn();
+      state->peerRequestSn      = requestParam->getSrReqSn();
+   }
+   else if (state->streamReset) {
+      resetExpectedSsns();
+      sctpEV3<<"processOutgoingResetRequestArrived: resetExpectedSsns\n";
+      sendStreamResetResponse(requestParam->getSrReqSn());
+   }
+}
+
+void SCTPAssociation::processIncomingResetRequestArrived(SCTPIncomingSSNResetRequestParameter* requestParam)
+{
+   sendOutgoingResetRequest(requestParam);
+   sctpEV3<<"processIncomingResetRequestArrived: sendOutgoingResetRequestArrived returned\n";
+   state->resetPending = true;
+}
+
+void SCTPAssociation::processSSNTSNResetRequestArrived(SCTPSSNTSNResetRequestParameter* requestParam)
+{
+   sctpEV3<<"processSSNTSNResetRequestArrived\n";
+   state->advancedPeerAckPoint = state->nextTSN-1;
+   state->stopOldData = true;
+   sendStreamResetResponse(requestParam, true);
+}
+
+
+void SCTPAssociation::processResetResponseArrived(SCTPStreamResetResponseParameter* responseParam)
+{
+   sctpEV3<<"processResetResponseArrived \n";
+   if (getPath(remoteAddr)->ResetTimer->isScheduled())
+   {
+      SCTPResetTimer* tm = check_and_cast<SCTPResetTimer*>(PK(getPath(remoteAddr)->ResetTimer)->decapsulate());
+      sctpEV3<<"SrResSn="<<responseParam->getSrResSn()<<" tmOut="<<tm->getOutSN()<<" tmIn= "<<tm->getInSN()<<"\n";
+      if (tm->getOutSN() == responseParam->getSrResSn() || tm->getInSN() == responseParam->getSrResSn())
+      {
+         stopTimer(getPath(remoteAddr)->ResetTimer);
+         delete state->resetChunk;
+      }
+   }
+   if (responseParam->getResult()==PERFORMED)
+   {
+      resetSsns();
+      state->resetPending = false;
+      if (responseParam->getReceiversNextTsn() != 0)
+      {
+         state->nextTSN    = responseParam->getReceiversNextTsn();
+         state->lastTsnAck = responseParam->getReceiversNextTsn() - 1;
+         state->gapList.forwardCumAckTSN(responseParam->getSendersNextTsn() - 1);
+         state->peerTsnAfterReset = responseParam->getSendersNextTsn();
+         state->stopReceiving      = false;
+         state->stopOldData        = true;
+         sendSack();
+      }
+      sendIndicationToApp(SCTP_I_SEND_STREAMS_RESETTED);
+
+   }
+   else
+   {
+      sctpEV3<<"Reset Request failed. Send indication to app.\n";
+      state->resetPending = false;
+      sendIndicationToApp(SCTP_I_RESET_REQUEST_FAILED);
+   }
+}
+
+
+SCTPEventCode SCTPAssociation::processInAndOutResetRequestArrived(SCTPIncomingSSNResetRequestParameter* inRequestParam,
+                                                                  SCTPOutgoingSSNResetRequestParameter* outRequestParam)
+{
+   if (tsnGt(outRequestParam->getLastTsn(), state->gapList.getHighestTSNReceived())) {
+      state->lastTsnBeforeReset = outRequestParam->getLastTsn();
+      state->peerRequestSn      = outRequestParam->getSrReqSn();
+      state->inRequestSn        = inRequestParam->getSrReqSn();
+      state->inOut              = true;
+   }
+   else {
+      resetExpectedSsns();
+      sctpEV3<<"processInAndOutResetRequestArrived: resetExpectedSsns\n";
+      sendOutgoingRequestAndResponse(inRequestParam->getSrReqSn(), outRequestParam->getSrReqSn());
+   }
+   state->resetPending = true;
+   return SCTP_E_IGNORE;
+}
+
+
+SCTPEventCode SCTPAssociation::processOutAndResponseArrived(SCTPOutgoingSSNResetRequestParameter* outRequestParam,
+                                                            SCTPStreamResetResponseParameter*     responseParam)
+{
+   sctpEV3<<"processOutAndResponseArrived\n";
+   if (getPath(remoteAddr)->ResetTimer->isScheduled())
+   {
+      SCTPResetTimer* tm = check_and_cast<SCTPResetTimer*>(PK(getPath(remoteAddr)->ResetTimer)->decapsulate());
+      if (tm->getOutSN() == responseParam->getSrResSn() && tm->getInSN() == outRequestParam->getSrResSn())
+         stopTimer(getPath(remoteAddr)->ResetTimer);
+   }
+   sendStreamResetResponse(outRequestParam->getSrReqSn());
+   if (responseParam->getResult()==PERFORMED)
+   {
+      resetSsns();
+      resetExpectedSsns();
+      state->resetPending = false;
+      sendIndicationToApp(SCTP_I_SEND_STREAMS_RESETTED);
+   }
+   else
+   {
+      sctpEV3<<"Reset Request failed. Send indication to app.\n";
+      state->resetPending = false;
+      sendIndicationToApp(SCTP_I_RESET_REQUEST_FAILED);
+   }
+   return SCTP_E_IGNORE;
+}
+
+
+SCTPEventCode SCTPAssociation::processStreamResetArrived(SCTPStreamResetChunk* resetChunk)
+{
+   SCTPParameter * parameter;
+   bool requestReceived = false;
+   uint32 numberOfParameters = resetChunk->getParametersArraySize();
+   sctpEV3<<"processStreamResetArrived\n";
+
+   parameter=(SCTPParameter*)(resetChunk->removeParameter());
+
+   switch (parameter->getParameterType())
+   {
+      case OUTGOING_RESET_REQUEST_PARAMETER:
+         SCTPOutgoingSSNResetRequestParameter* outRequestParam;
+         outRequestParam = check_and_cast<SCTPOutgoingSSNResetRequestParameter*>(parameter);
+         if (numberOfParameters == 1)
+         {
+            processOutgoingResetRequestArrived(outRequestParam);
+            delete parameter;
+         }
+         else
+         {
+            parameter=(SCTPParameter*)(resetChunk->removeParameter());
+            switch (parameter->getParameterType())
+            {
+               case INCOMING_RESET_REQUEST_PARAMETER:
+                  SCTPIncomingSSNResetRequestParameter* inRequestParam;
+                  inRequestParam = check_and_cast<SCTPIncomingSSNResetRequestParameter*>(parameter);
+                  processInAndOutResetRequestArrived(inRequestParam, outRequestParam);
+                  break;
+               case STREAM_RESET_RESPONSE_PARAMETER:
+                  SCTPStreamResetResponseParameter* responseParam;
+                  responseParam = check_and_cast<SCTPStreamResetResponseParameter*>(parameter);
+                  processOutAndResponseArrived(outRequestParam, responseParam);
+                  break;
             }
-        }
-    }
-    if (tsnGt(tsn, state->highestTsnReceived)) {
-        sctpEV3 << "highestTsnReceived=" << state->highestTsnReceived
-                  << " tsn=" << tsn << endl;
-        state->highestTsnReceived = state->highestTsnStored = tsn;
-        if (tsn == initPeerTsn) {
-            state->cTsnAck = tsn;
-        }
-        else {
-            sctpEV3 << "Update fragment list" << endl;
-            checkCtsnaChange = updateGapList(tsn);
-        }
-        state->newChunkReceived = true;
-    }
-    else if (tsnIsDuplicate(tsn)) {
-        // TSN value is duplicate within a fragment
-        sctpEV3 << "Duplicate TSN " << tsn << " (copy)" << endl;
-        state->dupList.push_back(tsn);
-        state->dupList.unique();
-        return SCTP_E_IGNORE;
-    }
-    else {
-        checkCtsnaChange = updateGapList(tsn);
-    }
-    if (state->swsAvoidanceInvoked) {
-        // swsAvoidanceInvoked => schedule a SACK to be sent at once in this case
-        sctpEV3 << "swsAvoidanceInvoked" << endl;
-        state->ackState = sackFrequency;
-    }
-
-    if (checkCtsnaChange) {
-        advanceCtsna();
-    }
-    sctpEV3 << "cTsnAck=" << state->cTsnAck
-              << " highestTsnReceived=" << state->highestTsnReceived << endl;
-
-    SCTPEventCode event = SCTP_E_SEND;
-    if (state->newChunkReceived) {
-        SCTPReceiveStreamMap::iterator iter = receiveStreams.find(dataChunk->getSid());
-        const int ret = iter->second->enqueueNewDataChunk(makeVarFromMsg(dataChunk));
-        if (ret > 0) {
-            state->queuedReceivedBytes+=payloadLength;
-
-            event = SCTP_E_DELIVERED;
-            if (ret < 3) {
-                sendDataArrivedNotification(dataChunk->getSid());
-                putInDeliveryQ(dataChunk->getSid());
+
+         }
+         requestReceived = true;
+         break;
+      case INCOMING_RESET_REQUEST_PARAMETER:
+         SCTPIncomingSSNResetRequestParameter* inRequestParam;
+         inRequestParam = check_and_cast<SCTPIncomingSSNResetRequestParameter*>(parameter);
+         if (numberOfParameters == 1)
+            processIncomingResetRequestArrived(inRequestParam);
+         else
+         {
+            parameter=(SCTPParameter*)(resetChunk->removeParameter());
+            if (parameter->getParameterType()==OUTGOING_RESET_REQUEST_PARAMETER)
+            {
+               SCTPOutgoingSSNResetRequestParameter* outRequestParam;
+               outRequestParam = check_and_cast<SCTPOutgoingSSNResetRequestParameter*>(parameter);
+               processInAndOutResetRequestArrived(inRequestParam, outRequestParam);
+            }
+
+         }
+         requestReceived = true;
+         break;
+      case SSN_TSN_RESET_REQUEST_PARAMETER:
+         SCTPSSNTSNResetRequestParameter* ssnRequestParam;
+         ssnRequestParam = check_and_cast<SCTPSSNTSNResetRequestParameter*>(parameter);
+         processSSNTSNResetRequestArrived(ssnRequestParam);
+         requestReceived = true;
+         break;
+      case STREAM_RESET_RESPONSE_PARAMETER:
+         SCTPStreamResetResponseParameter* responseParam;
+         responseParam = check_and_cast<SCTPStreamResetResponseParameter*>(parameter);
+         if (numberOfParameters == 1)
+         {
+            processResetResponseArrived(responseParam);
+            delete parameter;
+         }
+         else
+         {
+            parameter=(SCTPParameter*)(resetChunk->removeParameter());
+            switch (parameter->getParameterType())
+            {
+               case OUTGOING_RESET_REQUEST_PARAMETER:
+                  SCTPOutgoingSSNResetRequestParameter* outRequestParam;
+                  outRequestParam = check_and_cast<SCTPOutgoingSSNResetRequestParameter*>(parameter);
+                  processOutAndResponseArrived(outRequestParam, responseParam);
+                  requestReceived = true;
+                  break;
             }
-        }
-        state->newChunkReceived = false;
-    }
+         }
+         break;
+   }
+   if (requestReceived)
+      sendSack();
 
-    return(event);
+   return SCTP_E_IGNORE;
 }
 
 
-SCTPEventCode SCTPAssociation::processHeartbeatAckArrived(SCTPHeartbeatAckChunk* hback,
-                                                                             SCTPPathVariables*     path)
+SCTPEventCode SCTPAssociation::processAsconfArrived(SCTPAsconfChunk* asconfChunk)
+{
+   SCTPParameter* sctpParam;
+   SCTPPathVariables* path;
+   IPvXAddress addr;
+   std::vector<IPvXAddress> locAddr;
+   SCTPAuthenticationChunk* authChunk;
+   sctpEV3<<"Asconf arrived "<<asconfChunk->getName()<<"\n";
+   SCTPMessage *sctpAsconfAck = new SCTPMessage("ASCONF_ACK");
+   sctpAsconfAck->setBitLength(SCTP_COMMON_HEADER*8);
+   sctpAsconfAck->setSrcPort(localPort);
+   sctpAsconfAck->setDestPort(remotePort);
+   if (state->auth && state->peerAuth)
+   {
+      authChunk = createAuthChunk();
+      sctpAsconfAck->addChunk(authChunk);
+   }
+   if (state->numberAsconfReceived >0 || (state->numberAsconfReceived == 0 && asconfChunk->getSerialNumber()== initPeerTsn + state->numberAsconfReceived))
+   {
+      SCTPAsconfAckChunk* asconfAckChunk = createAsconfAckChunk(asconfChunk->getSerialNumber());
+      state->numberAsconfReceived++;
+      int32 count = asconfChunk->getAsconfParamsArraySize();
+      sctpEV3<<"Number of Asconf parameters="<<count<<"\n";
+      for (int32 c=0; c<count; c++)
+      {
+         sctpParam = (SCTPParameter*)(asconfChunk->removeAsconfParam());
+         switch (sctpParam->getParameterType())
+         {
+            case ADD_IP_ADDRESS:
+            sctpEV3<<"ADD_IP_PARAMETER\n";
+               SCTPAddIPParameter* ipParam;
+               ipParam = check_and_cast<SCTPAddIPParameter*>(sctpParam);
+               addr = ipParam->getAddressParam();
+               if (addr==IPvXAddress("0.0.0.0"))
+               {
+                  sctpEV3<<"no address specified, add natted address "<<remoteAddr<<"\n";
+                  addr = remoteAddr;
+                  sendIndicationToApp(SCTP_I_ADDRESS_ADDED);
+               }
+               for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k)
+               {
+                  sctpMain->addRemoteAddress(this,(*k), addr);
+                  addPath(addr);
+                  sctpEV3<<"add remote address "<<addr<<" to local address "<<(*k)<<"\n";
+               }
+               this->remoteAddressList.push_back(addr);
+               path = getPath(addr);
+               if (state->enableHeartbeats)
+               {
+                  stopTimer(path->HeartbeatTimer);
+                  stopTimer(path->HeartbeatIntervalTimer);
+                  path->statisticsPathRTO->record(path->pathRto);
+                  startTimer(path->HeartbeatIntervalTimer, 0.1);
+                  path->forceHb = true;
+               }
+               else
+                  path->confirmed = true;
+               asconfAckChunk->addAsconfResponse(createSuccessIndication(ipParam->getRequestCorrelationId()));
+               delete ipParam;
+               break;
+            case DELETE_IP_ADDRESS:
+            sctpEV3<<"DELETE_IP_PARAMETER\n";
+               SCTPDeleteIPParameter* delParam;
+               delParam = check_and_cast<SCTPDeleteIPParameter*>(sctpParam);
+               addr = delParam->getAddressParam();
+               if (state->localAddresses.size() == 1)
+               {
+                  SCTPErrorCauseParameter* errorParam;
+                  errorParam = new SCTPErrorCauseParameter("Error");
+                  errorParam->setParameterType(ERROR_CAUSE_INDICATION);
+                  errorParam->setResponseCorrelationId(delParam->getRequestCorrelationId());
+                  errorParam->setErrorCauseType(ERROR_DELETE_LAST_IP_ADDRESS);
+                  errorParam->setBitLength((SCTP_ADD_IP_PARAMETER_LENGTH+4)*8);
+                  errorParam->encapsulate((cPacket*)delParam->dup());
+                  asconfAckChunk->addAsconfResponse(errorParam);
+               }
+               else if (addr == remoteAddr)
+               {
+                  sctpEV3<<"addr=remoteAddr, make Error Parameter\n";
+                  SCTPErrorCauseParameter* errParam;
+                  errParam = new SCTPErrorCauseParameter("Error");
+                  errParam->setParameterType(ERROR_CAUSE_INDICATION);
+                  errParam->setResponseCorrelationId(delParam->getRequestCorrelationId());
+                  errParam->setErrorCauseType(ERROR_DELETE_SOURCE_ADDRESS);
+                  errParam->setBitLength((SCTP_ADD_IP_PARAMETER_LENGTH+4)*8);
+                  errParam->encapsulate((cPacket*)delParam->dup());
+                  asconfAckChunk->addAsconfResponse(errParam);
+               }
+               else
+               {
+                  locAddr = (std::vector<IPvXAddress>) state->localAddresses;
+                  sctpMain->removeRemoteAddressFromAllAssociations(this,addr, locAddr);
+                  for (AddressVector::iterator j=remoteAddressList.begin(); j!=remoteAddressList.end(); j++)
+                  {
+                     if ((*j)==addr)
+                     {
+                        remoteAddressList.erase(j);
+                        break;
+                     }
+                  }
+                  removePath(addr);
+                  sctpEV3<<"remove path from address "<<addr<<"\n";
+                  asconfAckChunk->addAsconfResponse(createSuccessIndication(delParam->getRequestCorrelationId()));
+               }
+               break;
+            case SET_PRIMARY_ADDRESS:
+               sctpEV3<<"SET_PRIMARY_ADDRESS\n";
+               SCTPSetPrimaryIPParameter* priParam;
+               priParam = check_and_cast<SCTPSetPrimaryIPParameter*>(sctpParam);
+               addr = priParam->getAddressParam();
+               if (addr==IPvXAddress("0.0.0.0"))
+               {
+                  sctpEV3<<"no address specified, add natted address "<<remoteAddr<<"\n";
+                  addr = remoteAddr;
+               }
+               for (AddressVector::iterator i = remoteAddressList.begin(); i!= remoteAddressList.end();i++)
+               {
+                  if ((*i) == addr)
+                  {
+                     if (getPath(addr)->confirmed == true)
+                     {
+                        state->setPrimaryPath(getPath(addr));
+                        sctpEV3<<"set primaryPath to "<<addr<<"\n";
+                     }
+                     else
+                     {
+                        getPath(addr)->primaryPathCandidate = true;
+                        //if (state->enableHeartbeats)
+                        sendHeartbeat(getPath(addr));
+                     }
+                     break;
+                  }
+               }
+               asconfAckChunk->addAsconfResponse(createSuccessIndication(priParam->getRequestCorrelationId()));
+               break;
+         }
+      }
+      sctpAsconfAck->addChunk(asconfAckChunk);
+      sendToIP(sctpAsconfAck, remoteAddr);
+   }
+   return SCTP_E_IGNORE;
+}
+
+SCTPEventCode SCTPAssociation::processAsconfAckArrived(SCTPAsconfAckChunk* asconfAckChunk)
+{
+   SCTPParameter* sctpParam;
+   IPvXAddress addr;
+   SCTPAsconfChunk *sctpasconf;
+   std::vector<uint32> errorCorrId;
+   std::vector<uint32>::iterator iter;
+   bool errorFound = false;
+
+   sctpasconf=check_and_cast<SCTPAsconfChunk*>(state->asconfChunk->dup());
+   if (asconfAckChunk->getSerialNumber()==sctpasconf->getSerialNumber())
+   {
+      stopTimer(getPath(remoteAddr)->AsconfTimer);
+      state->errorCount = 0;
+      state->asconfOutstanding = false;
+      getPath(remoteAddr)->pathErrorCount = 0;
+      std::vector<IPvXAddress> remAddr = (std::vector<IPvXAddress>) remoteAddressList;
+      for (uint32 j=0; j<asconfAckChunk->getAsconfResponseArraySize(); j++)
+      {
+         sctpParam = (SCTPParameter*)(asconfAckChunk->getAsconfResponse(j));
+         if (sctpParam->getParameterType() == ERROR_CAUSE_INDICATION)
+         {
+            SCTPErrorCauseParameter* error = check_and_cast<SCTPErrorCauseParameter* >(sctpParam);
+            errorCorrId.push_back(error->getResponseCorrelationId());
+            sctpEV3<<"error added with id "<<error->getResponseCorrelationId()<<"\n";
+         }
+      }
+      for (uint32 i=0; i<sctpasconf->getAsconfParamsArraySize(); i++)
+      {
+         sctpParam = check_and_cast<SCTPParameter*>(sctpasconf->removeAsconfParam());
+         errorFound = false;
+         switch (sctpParam->getParameterType())
+         {
+            case ADD_IP_ADDRESS:
+               SCTPAddIPParameter* ipParam;
+               ipParam = check_and_cast<SCTPAddIPParameter*>(sctpParam);
+               if (errorCorrId.size()>0)
+               {
+                  for (iter = errorCorrId.begin(); iter != errorCorrId.end(); iter++)
+                     if ((*iter) == ipParam->getRequestCorrelationId())
+                     {
+                        errorFound = true;
+                        break;
+                     }
+               }
+               if (errorFound) break;
+               addr = ipParam->getAddressParam();
+               if (addr==IPvXAddress("0.0.0.0"))
+               {
+                  addr = localAddr;
+                  sendIndicationToApp(SCTP_I_ADDRESS_ADDED);
+               }
+               sctpMain->addLocalAddressToAllRemoteAddresses(this, addr, remAddr);
+               state->localAddresses.push_back(addr);
+               delete ipParam;
+               break;
+            case DELETE_IP_ADDRESS:
+               SCTPDeleteIPParameter* delParam;
+               delParam = check_and_cast<SCTPDeleteIPParameter*>(sctpParam);
+               if (errorCorrId.size()>0)
+               {
+                  for (iter = errorCorrId.begin(); iter != errorCorrId.end(); iter++)
+                  {
+                     if ((*iter) == delParam->getRequestCorrelationId())
+                     {
+                        errorFound = true;
+                        break;
+                     }
+                  }
+               }
+               if (errorFound==true) break;
+               addr = delParam->getAddressParam();
+               sctpMain->removeLocalAddressFromAllRemoteAddresses(this, addr, remAddr);
+               for (AddressVector::iterator j=state->localAddresses.begin(); j!=state->localAddresses.end(); j++)
+               {
+                  if ((*j)==addr)
+                  {
+                     sctpEV3<<"erase address "<<(*j)<<"\n";
+                     state->localAddresses.erase(j);
+                     break;
+                  }
+               }
+               break;
+         }
+      }
+   }
+   delete sctpasconf;
+   return SCTP_E_IGNORE;
+}
+
+bool SCTPAssociation::processPacketDropArrived(SCTPPacketDropChunk* packetDropChunk)
 {
-    path->numberOfHeartbeatAcksRcvd++;
-    path->pathRcvdHbAck->record(path->numberOfHeartbeatAcksRcvd);
-    /* hb-ack goes to pathmanagement, reset error counters, stop timeout timer */
-    const IPvXAddress addr          = hback->getRemoteAddr();
-    const simtime_t hbTimeField = hback->getTimeField();
-    stopTimer(path->HeartbeatTimer);
-    /* assume a valid RTT measurement on this path */
-    simtime_t rttEstimation = simTime() - hbTimeField;
-    pmRttMeasurement(path, rttEstimation);
-    pmClearPathCounter(path);
-    path->confirmed = true;
-    path->lastAckTime = simTime();
-    if (path->primaryPathCandidate == true) {
-        state->setPrimaryPath(getPath(addr));
-        path->primaryPathCandidate = false;
-        if (path->pmtu < state->assocPmtu) {
-            state->assocPmtu = path->pmtu;
-        }
-        path->ssthresh = state->peerRwnd;
-        recordCwndUpdate(path);
-        path->heartbeatTimeout = (double)sctpMain->par("hbInterval") + path->pathRto;
-    }
-
-    if (path->activePath == false) {
-        sctpEV3 << "HB ACK arrived activePath=false. remoteAddress=" <<path->remoteAddress
-                  << " initialPP=" << state->initialPrimaryPath << endl;
-        path->activePath = true;
-        if (state->reactivatePrimaryPath && path->remoteAddress == state->initialPrimaryPath) {
-            state->setPrimaryPath(path);
-        }
-        sctpEV3 << "primaryPath now " << state->getPrimaryPathIndex() << endl;
-    }
-    sctpEV3 << "Received HB ACK chunk...resetting error counters on path " << addr
-              <<", rttEstimation=" << rttEstimation << endl;
-    path->pathErrorCount = 0;
-    return SCTP_E_IGNORE;
+   bool dataReceived = false;
+
+   if (packetDropChunk->getMFlag() == false) {
+      sctpEV3 << "processPacketDropArrived" << endl;
+      if(SCTP::checkQueues) {
+         checkOutstandingBytes();
+      }
+      if (packetDropChunk->getEncapsulatedPacket() != NULL) {
+         SCTPMessage* sctpmsg        = (SCTPMessage*)(packetDropChunk->decapsulate());
+         const uint32 numberOfChunks = sctpmsg->getChunksArraySize();
+         sctpEV3 << "numberOfChunks=" << numberOfChunks << endl;
+         for (uint32 i = 0; i < numberOfChunks; i++) {
+            SCTPChunk*  chunk = (SCTPChunk*)(sctpmsg->removeChunk());
+            const uint8 type  = chunk->getChunkType();
+            switch (type) {
+               case DATA:
+               {
+                  SCTPDataChunk* dataChunk = check_and_cast<SCTPDataChunk*>(chunk);
+                  const uint32   tsn       = dataChunk->getTsn();
+                  SCTPQueue::PayloadQueue::iterator pq;
+                  pq = retransmissionQ->payloadQueue.find(tsn);
+                  if( (pq != retransmissionQ->payloadQueue.end()) &&
+                      (!chunkHasBeenAcked(pq->second)) ) {
+                     sctpEV3 << simTime() << ": Packet Drop for TSN "
+                             << pq->second->tsn << " on path "
+                             << pq->second->getLastDestination()
+                             << " -> transmitting it again" << endl;
+                     putInTransmissionQ(pq->first, pq->second);
+                  }
+                  delete dataChunk->decapsulate();
+                  dataReceived = true;
+                  break;
+               }
+               case SACK:
+                  sendSack();
+                  break;
+               case INIT:
+                  stopTimer(T1_InitTimer);
+                  retransmitInit();
+                  startTimer(T1_InitTimer,state->initRexmitTimeout);
+                  break;
+               case HEARTBEAT:
+                  sendHeartbeat(getPath(remoteAddr));
+                  break;
+               case HEARTBEAT_ACK:
+                  break;
+               case SHUTDOWN:
+                  stopTimer(T2_ShutdownTimer);
+                  retransmitShutdown();
+                  startTimer(T2_ShutdownTimer,state->initRexmitTimeout);
+                  break;
+               case SHUTDOWN_ACK:
+                  stopTimer(T2_ShutdownTimer);
+                  retransmitShutdownAck();
+                  startTimer(T2_ShutdownTimer,state->initRexmitTimeout);
+                  break;
+               case COOKIE_ECHO:
+                  stopTimer(T1_InitTimer);
+                  retransmitCookieEcho();
+                  startTimer(T1_InitTimer,state->initRexmitTimeout);
+                  break;
+               case COOKIE_ACK:
+                  sendCookieAck(remoteAddr);
+                  break;
+               case ASCONF:
+                  stopTimer(getPath(remoteAddr)->AsconfTimer);
+                  retransmitAsconf();
+                  startTimer(getPath(remoteAddr)->AsconfTimer, getPath(remoteAddr)->pathRto);
+                  break;
+               case FORWARD_TSN:
+               {
+                  if (peekAbandonedChunk(getPath(remoteAddr))!=NULL)
+                  {
+                     SCTPMessage* sctpmsg = new SCTPMessage();
+                     sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+                     SCTPForwardTsnChunk* forwardChunk = createForwardTsnChunk(remoteAddr);
+                     if (state->auth && state->peerAuth && typeInChunkList(FORWARD_TSN))
+                     {
+                        SCTPAuthenticationChunk* authChunk = createAuthChunk();
+                        sctpmsg->addChunk(authChunk);
+                     }
+                     sctpmsg->addChunk(forwardChunk);
+                  }
+                  break;
+               }
+            }
+            delete chunk;
+         }
+         disposeOf(sctpmsg);
+      }
+      else {
+         sctpEV3 << "no chunk encapsulated" << endl;
+      }
+      state->peerRwnd = packetDropChunk->getMaxRwnd() -
+                           packetDropChunk->getQueuedData() -
+                           getOutstandingBytes();
+      statisticsPeerRwnd->record(state->peerRwnd);
+      if(SCTP::checkQueues) {
+         checkOutstandingBytes();
+      }
+      return dataReceived;
+   }
+   return false;
 }
 
 
+void SCTPAssociation::processErrorArrived(SCTPErrorChunk* errorChunk)
+{
+   uint32 parameterType;
+   for (uint32 i=0; i<errorChunk->getParametersArraySize(); i++)
+   {
+      SCTPParameter* param = (SCTPParameter*)errorChunk->getParameters(i);
+      parameterType  = param->getParameterType();
+      switch (parameterType)
+      {
+         case MISSING_NAT_ENTRY:
+         {
+            if (((bool)sctpMain->par("addIP")) && (StartAddIP->isScheduled()))
+            {
+               stopTimer(StartAddIP);
+               state->corrIdNum = state->asconfSn;
+               const char* type = (const char *)sctpMain->par("addIpType");
+               sendAsconf(type, true);
+            }
+            break;
+         }
+      }
+   }
+}
+
 
 void SCTPAssociation::process_TIMEOUT_INIT_REXMIT(SCTPEventCode& event)
 {
-    if (++state->initRetransCounter > (int32)sctpMain->par("maxInitRetrans"))
-    {
-        sctpEV3 << "Retransmission count during connection setup exceeds " << (int32)sctpMain->par("maxInitRetrans") << ", giving up\n";
-        sendIndicationToApp(SCTP_I_CLOSED);
-        sendAbort();
-        sctpMain->removeAssociation(this);
-        return;
-    }
-    sctpEV3<< "Performing retransmission #" << state->initRetransCounter << "\n";
-    switch(fsm->getState())
-    {
-        case SCTP_S_COOKIE_WAIT: retransmitInit(); break;
-        case SCTP_S_COOKIE_ECHOED: retransmitCookieEcho(); break;
-        default:     opp_error("Internal error: INIT-REXMIT timer expired while in state %s", stateName(fsm->getState()));
-    }
-    state->initRexmitTimeout *= 2;
-    if (state->initRexmitTimeout > SCTP_TIMEOUT_INIT_REXMIT_MAX)
-        state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT_MAX;
-    startTimer(T1_InitTimer,state->initRexmitTimeout);
+   if (++state->initRetransCounter > (int32)sctpMain->par("maxInitRetrans"))
+   {
+      sctpEV3 << "Retransmission count during connection setup exceeds " << (int32)sctpMain->par("maxInitRetrans") << ", giving up\n";
+      sendIndicationToApp(SCTP_I_CLOSED);
+      sendAbort();
+      sctpMain->removeAssociation(this);
+      return;
+   }
+   sctpEV3<< "Performing retransmission #" << state->initRetransCounter << "\n";
+   switch(fsm->getState())
+   {
+      case SCTP_S_COOKIE_WAIT: retransmitInit(); break;
+      case SCTP_S_COOKIE_ECHOED: retransmitCookieEcho(); break;
+      default:  opp_error("Internal error: INIT-REXMIT timer expired while in state %s", stateName(fsm->getState()));
+   }
+   state->initRexmitTimeout *= 2;
+   if (state->initRexmitTimeout > SCTP_TIMEOUT_INIT_REXMIT_MAX)
+      state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT_MAX;
+   startTimer(T1_InitTimer,state->initRexmitTimeout);
 }
 
 void SCTPAssociation::process_TIMEOUT_SHUTDOWN(SCTPEventCode& event)
 {
 
-    if (++state->errorCount > (uint32)sctpMain->par("assocMaxRetrans"))
-    {
-        sendIndicationToApp(SCTP_I_CONN_LOST);
-        sendAbort();
-        sctpMain->removeAssociation(this);
-        return;
-    }
-
-    sctpEV3 << "Performing shutdown retransmission. Assoc error count now "<<state->errorCount<<" \n";
-    if(fsm->getState() == SCTP_S_SHUTDOWN_SENT)
-    {
-        retransmitShutdown();
-    }
-    else if (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)
-        retransmitShutdownAck();
-
-    state->initRexmitTimeout *= 2;
-    if (state->initRexmitTimeout > SCTP_TIMEOUT_INIT_REXMIT_MAX)
-        state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT_MAX;
-    startTimer(T2_ShutdownTimer,state->initRexmitTimeout);
+   if (++state->errorCount > (uint32)sctpMain->par("assocMaxRetrans"))
+   {
+      sendIndicationToApp(SCTP_I_CONN_LOST);
+      sendAbort();
+      sctpMain->removeAssociation(this);
+      return;
+   }
+
+   sctpEV3 << "Performing shutdown retransmission. Assoc error count now "<<state->errorCount<<" \n";
+   if(fsm->getState() == SCTP_S_SHUTDOWN_SENT)
+   {
+      retransmitShutdown();
+   }
+   else if (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)
+      retransmitShutdownAck();
+
+   state->initRexmitTimeout *= 2;
+   if (state->initRexmitTimeout > SCTP_TIMEOUT_INIT_REXMIT_MAX)
+      state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT_MAX;
+   startTimer(T2_ShutdownTimer,state->initRexmitTimeout);
 }
 
 
 void SCTPAssociation::process_TIMEOUT_HEARTBEAT_INTERVAL(SCTPPathVariables* path, bool force)
 {
-
-    sctpEV3<<"HB Interval timer expired -- sending new HB REQ on path "<<path->remoteAddress<<"\n";
-    /* restart hb_send_timer on this path */
-    stopTimer(path->HeartbeatIntervalTimer);
-    stopTimer(path->HeartbeatTimer);
-    path->heartbeatIntervalTimeout = (double)sctpMain->par("hbInterval") +  path->pathRto;
-    path->heartbeatTimeout = path->pathRto;
-    startTimer(path->HeartbeatIntervalTimer, path->heartbeatIntervalTimeout);
-    if ((simTime() - path->lastAckTime > path->heartbeatIntervalTimeout/2) || path->forceHb)
-    {
-        sendHeartbeat(path);
-        startTimer(path->HeartbeatTimer, path->heartbeatTimeout);
-
-        path->forceHb = false;
-    }
+   sctpEV3<<"HB Interval timer expired -- sending new HB REQ on path "<<path->remoteAddress<<"\n";
+   /* restart hb_send_timer on this path */
+   stopTimer(path->HeartbeatIntervalTimer);
+   stopTimer(path->HeartbeatTimer);
+   path->heartbeatIntervalTimeout = (double)sctpMain->par("hbInterval") +  path->pathRto;
+   path->heartbeatTimeout = path->pathRto;
+   startTimer(path->HeartbeatIntervalTimer, path->heartbeatIntervalTimeout);
+
+   if (state->enableHeartbeats && (simTime() - path->lastAckTime > path->heartbeatIntervalTimeout/2 || path->forceHb || state->sendHeartbeatsOnActivePaths))
+   {
+      sendHeartbeat(path);
+      startTimer(path->HeartbeatTimer, path->heartbeatTimeout);
+
+      path->forceHb = false;
+   }
 }
 
 
 void SCTPAssociation::process_TIMEOUT_HEARTBEAT(SCTPPathVariables* path)
 {
-    bool oldState;
-
-    /* check if error counters must be increased */
-      if (path->activePath)
+   bool oldState;
+
+   /* check if error counters must be increased */
+     if (path->activePath)
+     {
+          state->errorCount++;
+          path->pathErrorCount++;
+
+          sctpEV3<<"HB timeout timer expired for path "<<path->remoteAddress<<" --> Increase Error Counters (Assoc: "<<state->errorCount<<", Path: "<<path->pathErrorCount<<")\n";
+     }
+
+   /* RTO must be doubled for this path ! */
+   path->pathRto = (simtime_t)min(2 * path->pathRto.dbl(), sctpMain->par("rtoMax"));
+   path->statisticsPathRTO->record(path->pathRto);
+   /* check if any thresholds are exceeded, and if so, check if ULP must be notified */
+   if (state->errorCount > (uint32)sctpMain->par("assocMaxRetrans"))
+   {
+      sendIndicationToApp(SCTP_I_CONN_LOST);
+      sendAbort();
+      sctpMain->removeAssociation(this);
+      return;
+   }
+   else
+   {
+      /* set path state to INACTIVE, if the path error counter is exceeded */
+      if (path->pathErrorCount >  (uint32)sctpMain->par("pathMaxRetrans"))
       {
-             state->errorCount++;
-             path->pathErrorCount++;
-
-             sctpEV3<<"HB timeout timer expired for path "<<path->remoteAddress<<" --> Increase Error Counters (Assoc: "<<state->errorCount<<", Path: "<<path->pathErrorCount<<")\n";
-      }
-
-    /* RTO must be doubled for this path ! */
-    path->pathRto = (simtime_t)min(2 * path->pathRto.dbl(), sctpMain->par("rtoMax"));
-    path->statisticsPathRTO->record(path->pathRto);
-    /* check if any thresholds are exceeded, and if so, check if ULP must be notified */
-    if (state->errorCount > (uint32)sctpMain->par("assocMaxRetrans"))
-    {
-        sendIndicationToApp(SCTP_I_CONN_LOST);
-        sendAbort();
-        sctpMain->removeAssociation(this);
-        return;
-    }
-    else
-    {
-        /* set path state to INACTIVE, if the path error counter is exceeded */
-        if (path->pathErrorCount >   (uint32)sctpMain->par("pathMaxRetrans"))
-        {
-            oldState = path->activePath;
-            path->activePath = false;
-            if (path == state->getPrimaryPath()) {
-                state->setPrimaryPath(getNextPath(path));
-            }
-            sctpEV3 << "pathErrorCount now "<< path->pathErrorCount
-                      << "; PP now " << state->getPrimaryPathIndex() << endl;
-        }
-        /* then: we can check, if all paths are INACTIVE ! */
-        if (allPathsInactive())
-        {
-            sctpEV3<<"sctp_do_hb_to_timer() : ALL PATHS INACTIVE --> closing ASSOC\n";
-            sendIndicationToApp(SCTP_I_CONN_LOST);
-            return;
+         oldState = path->activePath;
+         path->activePath = false;
+         if (path == state->getPrimaryPath()) {
+            state->setPrimaryPath(getNextPath(path));
+         }
+         sctpEV3 << "pathErrorCount now "<< path->pathErrorCount
+                 << "; PP now " << state->getPrimaryPathIndex() << endl;
+      }
+      /* then: we can check, if all paths are INACTIVE ! */
+      if (allPathsInactive())
+      {
+         sctpEV3<<"sctp_do_hb_to_timer() : ALL PATHS INACTIVE --> closing ASSOC\n";
+         sendIndicationToApp(SCTP_I_CONN_LOST);
+         return;
 
-        } else if (path->activePath == false && oldState == true)
-        {
-            /* notify the application, in case the PATH STATE has changed from ACTIVE to INACTIVE */
-            pathStatusIndication(path, false);
-        }
+      } else if (path->activePath == false && oldState == true)
+      {
+         /* notify the application, in case the PATH STATE has changed from ACTIVE to INACTIVE */
+         pathStatusIndication(path, false);
+      }
 
-    }
+   }
 }
 
 void SCTPAssociation::stopTimers()
 {
-    for (SCTPPathMap::iterator j = sctpPathMap.begin(); j != sctpPathMap.end(); j++) {
-        stopTimer(j->second->HeartbeatTimer);
-        stopTimer(j->second->HeartbeatIntervalTimer);
-    }
+   for (SCTPPathMap::iterator j = sctpPathMap.begin(); j != sctpPathMap.end(); j++) {
+      stopTimer(j->second->HeartbeatTimer);
+      stopTimer(j->second->HeartbeatIntervalTimer);
+   }
 }
 
 void SCTPAssociation::stopTimer(cMessage* timer)
 {
-
-    ev << "stopTimer " << timer->getName() << endl;
-    if (timer->isScheduled()) {
-        cancelEvent(timer);
-    }
+   sctpEV3 << "stopTimer " << timer->getName() << endl;
+   if (timer->isScheduled()) {
+      cancelEvent(timer);
+   }
 }
 
 void SCTPAssociation::startTimer(cMessage* timer, const simtime_t& timeout)
 {
-    sctpEV3 << "startTimer " << timer->getName() << " with timeout "
-              << timeout << " to expire at " << simTime() + timeout << endl;
-    scheduleTimeout(timer, timeout);
+   sctpEV3 << "startTimer " << timer->getName() << " with timeout "
+           << timeout << " to expire at " << simTime() + timeout << endl;
+   scheduleTimeout(timer, timeout);
 }
 
+void SCTPAssociation::process_TIMEOUT_RESET(SCTPPathVariables* path)
+{
+   int32 value;
+
+   if ((value=updateCounters(path))== 1)
+   {
+      sctpEV3 << "Performing timeout reset" << endl;
+      if(SCTP::checkQueues) {
+         checkOutstandingBytes();
+      }
+      retransmitReset();
+
+      /* increase the RTO (by doubling it) */
+      path->pathRto = min(2*path->pathRto.dbl(), sctpMain->par("rtoMax"));
+      path->statisticsPathRTO->record(path->pathRto);
+      startTimer(path->ResetTimer,path->pathRto);
+      if(SCTP::checkQueues) {
+         checkOutstandingBytes();
+      }
+   }
+}
 
 
 int32 SCTPAssociation::updateCounters(SCTPPathVariables* path)
 {
-    bool notifyUlp = false;
-    if (++state->errorCount >=   (uint32)sctpMain->par("assocMaxRetrans"))
-    {
-        sctpEV3 << "Retransmission count during connection setup exceeds " << (int32)sctpMain->par("assocMaxRetrans") << ", giving up\n";
-        sendIndicationToApp(SCTP_I_CLOSED);
-        sendAbort();
-        sctpMain->removeAssociation(this);
-        return 0;
-    }
-    else if (++path->pathErrorCount >=  (uint32)sctpMain->par("pathMaxRetrans"))
-    {
-        if (path->activePath)
-        {
+   bool notifyUlp = false;
+   if (++state->errorCount >=  (uint32)sctpMain->par("assocMaxRetrans"))
+   {
+      sctpEV3 << "Retransmission count during connection setup exceeds " << (int32)sctpMain->par("assocMaxRetrans") << ", giving up\n";
+      sendIndicationToApp(SCTP_I_CLOSED);
+      sendAbort();
+      sctpMain->removeAssociation(this);
+      return 0;
+   }
+   else if (++path->pathErrorCount >=  (uint32)sctpMain->par("pathMaxRetrans"))
+   {
+      if (path->activePath)
+      {
+         /* tell the source */
+         notifyUlp = true;
+      }
+
+      path->activePath = false;
+      if (path == state->getPrimaryPath()) {
+         state->setPrimaryPath(getNextPath(path));
+      }
+      sctpEV3<<"process_TIMEOUT_RESET("<<(path->remoteAddress)<<") : PATH ERROR COUNTER EXCEEDED, path status is INACTIVE\n";
+      if (allPathsInactive())
+      {
+         sctpEV3<<"process_TIMEOUT_RESET : ALL PATHS INACTIVE --> closing ASSOC\n";
+         sendIndicationToApp(SCTP_I_CONN_LOST);
+         sendAbort();
+         sctpMain->removeAssociation(this);
+         return 0;
+      }
+      else if (notifyUlp)
+      {
+         /* notify the application */
+         pathStatusIndication(path, false);
+      }
+      sctpEV3<<"process_TIMEOUT_RESET("<<(path->remoteAddress)<<") : PATH ERROR COUNTER now "<<path->pathErrorCount<<"\n";
+      return 2;
+   }
+   return 1;
+}
+
+void SCTPAssociation::process_TIMEOUT_ASCONF(SCTPPathVariables* path)
+{
+   int32 value;
+
+   if ((value=updateCounters(path))== 1)
+   {
+      retransmitAsconf();
+
+      /* increase the RTO (by doubling it) */
+      path->pathRto = min(2*path->pathRto.dbl(),sctpMain->par("rtoMax"));
+      path->statisticsPathRTO->record(path->pathRto);
+
+      startTimer(path->AsconfTimer,path->pathRto);
+   }
+}
+
+
+void SCTPAssociation::process_TIMEOUT_RTX(SCTPPathVariables* path)
+{
+   sctpEV3 << "Processing retransmission timeout ..." << endl;
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+
+
+   // ====== Increase the RTO (by doubling it) ==============================
+   path->pathRto = min(2 * path->pathRto.dbl(), sctpMain->par("rtoMax"));
+   path->statisticsPathRTO->record(path->pathRto);
+   sctpEV3 << "T3 based retransmission for path " << path->remoteAddress
+           << endl;
+   if(SCTP::testing) {
+      sctpEV3 << "Unacked chunks in Retransmission Queue:" << endl;
+      for(SCTPQueue::PayloadQueue::const_iterator iterator = retransmissionQ->payloadQueue.begin();
+         iterator != retransmissionQ->payloadQueue.end(); ++iterator) {
+         const SCTPDataVariables* myChunk = iterator->second;
+         if(!myChunk->hasBeenAcked) {
+            const SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath();
+            sctpEV3 << " - "     << myChunk->tsn
+                    << "\tsent=now-"  << simTime() - myChunk->sendTime
+                    << "\tlast="  << myChunkLastPath->remoteAddress
+                    << "\tnumTX=" << myChunk->numberOfTransmissions
+                    << "\tnumRTX=" << myChunk->numberOfRetransmissions
+                    << "\tfastRTX=" << ((myChunk->hasBeenFastRetransmitted == true) ? "YES!" : "no")
+                    << endl;
+         }
+      }
+      sctpEV3 << "----------------------" << endl;
+   }
+
+   // ====== Update congestion window =======================================
+   (this->*ccFunctions.ccUpdateAfterRtxTimeout)(path);
+
+
+   // ====== Error Counter Handling =========================================
+   if (!state->zeroWindowProbing) {
+      state->errorCount++;
+      path->pathErrorCount++;
+      sctpEV3 << "RTX-Timeout: errorCount increased to "<<path->pathErrorCount<<"  state->errorCount="<<state->errorCount<<"\n";
+   }
+   if (state->errorCount >= (uint32)sctpMain->par("assocMaxRetrans")) {
+      /* error counter exceeded terminate the association -- create an SCTPC_EV_CLOSE event and send it to myself */
+
+      sctpEV3 << "process_TIMEOUT_RTX : ASSOC ERROR COUNTER EXCEEDED, closing ASSOC" << endl;
+      sendIndicationToApp(SCTP_I_CONN_LOST);
+      sendAbort();
+      sctpMain->removeAssociation(this);
+      return;
+   }
+   else {
+      if (path->pathErrorCount >= (uint32)sctpMain->par("pathMaxRetrans")) {
+         bool notifyUlp = false;
+
+         sctpEV3 << "pathErrorCount exceeded\n";
+         if (path->activePath) {
             /* tell the source */
             notifyUlp = true;
-        }
-
-        path->activePath = false;
-        if (path == state->getPrimaryPath()) {
-            state->setPrimaryPath(getNextPath(path));
-        }
-        sctpEV3<<"process_TIMEOUT_RESET("<<(path->remoteAddress)<<") : PATH ERROR COUNTER EXCEEDED, path status is INACTIVE\n";
-        if (allPathsInactive())
-        {
-            sctpEV3<<"process_TIMEOUT_RESET : ALL PATHS INACTIVE --> closing ASSOC\n";
+         }
+         path->activePath = false;
+         if (path->remoteAddress == state->getPrimaryPathIndex()) {
+            SCTPPathVariables* nextPath = getNextPath(path);
+            if (nextPath != NULL) {
+               state->setPrimaryPath(nextPath);
+            }
+         }
+         sctpEV3 << "process_TIMEOUT_RTX(" << path->remoteAddress
+                 << ") : PATH ERROR COUNTER EXCEEDED, path status is INACTIVE" << endl;
+         if (allPathsInactive()) {
+            sctpEV3 << "process_TIMEOUT_RTX: ALL PATHS INACTIVE --> connection LOST!" << endl;
             sendIndicationToApp(SCTP_I_CONN_LOST);
             sendAbort();
             sctpMain->removeAssociation(this);
-            return 0;
-        }
-        else if (notifyUlp)
-        {
-            /* notify the application */
+            return;
+         }
+         else if (notifyUlp) {
+            // Send notification to the application
             pathStatusIndication(path, false);
-        }
-        sctpEV3<<"process_TIMEOUT_RESET("<<(path->remoteAddress)<<") : PATH ERROR COUNTER now "<<path->pathErrorCount<<"\n";
-        return 2;
-    }
-    return 1;
-}
+         }
+      }
+      sctpEV3 << "process_TIMEOUT_RTX(" << path->remoteAddress
+              << ") : PATH ERROR COUNTER now " << path->pathErrorCount << endl;
+   }
+
+
+   // ====== Do Retransmission ==============================================
+   // dequeue all chunks not acked so far and put them in the TransmissionQ
+   if (!retransmissionQ->payloadQueue.empty()) {
+      sctpEV3 << "Still " << retransmissionQ->payloadQueue.size()
+              << " chunks in retransmissionQ" << endl;
+
+      for (SCTPQueue::PayloadQueue::iterator iterator = retransmissionQ->payloadQueue.begin();
+         iterator != retransmissionQ->payloadQueue.end(); iterator++) {
+         SCTPDataVariables* chunk = iterator->second;
+         assert(chunk != NULL);
+
+         // ====== Insert chunks into TransmissionQ ============================
+         // Only insert chunks that were sent to the path that has timed out
+         if ( ((chunkHasBeenAcked(chunk) == false && (chunk->countsAsOutstanding || chunk->hasBeenAbandoned))
+              || chunk->hasBeenReneged) &&(chunk->getLastDestinationPath() == path) ) {
+            SCTPPathVariables* nextPath = getNextDestination(chunk);
+#ifdef RCVMESSAGE_DEBUG
+            if(iterator == retransmissionQ->payloadQueue.begin()) {
+               std::cout << simTime() << ": Timer-Based RTX for TSN " << chunk->tsn
+                        << ": lastDestination=" << chunk->getLastDestination()
+                        << " lastPathRTO="      << chunk->getLastDestinationPath()->pathRto
+                        << " nextDestination="  << nextPath->remoteAddress
+                        << " nextPathRTO="      << nextPath->pathRto
+                        << " waiting="          << simTime() - chunk->sendTime
+                        << endl;
+            }
+#endif
+            sctpEV3 << simTime() << ": Timer-Based RTX for TSN " << chunk->tsn
+                  << ": lastDestination=" << chunk->getLastDestination()
+                  << " lastPathRTO="      << chunk->getLastDestinationPath()->pathRto
+                  << " nextDestination="  << nextPath->remoteAddress
+                  << " nextPathRTO="      << nextPath->pathRto
+                  << " waiting="          << simTime() - chunk->sendTime
+                  << endl;
+            chunk->getLastDestinationPath()->numberOfTimerBasedRetransmissions++;
+            chunk->hasBeenTimerBasedRtxed = true;
+            chunk->sendForwardIfAbandoned = true;
 
+            SCTP::AssocStatMap::iterator iter = sctpMain->assocStatMap.find(assocId);
+            iter->second.numT3Rtx++;
 
+            moveChunkToOtherPath(chunk, nextPath);
+         }
+      }
+   }
 
-int32 SCTPAssociation::process_TIMEOUT_RTX(SCTPPathVariables* path)
-{
-    sctpEV3 << "Processing retransmission timeout ..." << endl;
-
-    // ====== Increase the RTO (by doubling it) ==============================
-    path->pathRto = min(2 * path->pathRto.dbl(), sctpMain->par("rtoMax"));
-    path->statisticsPathRTO->record(path->pathRto);
-    sctpEV3 << "Schedule T3 based retransmission for path "<< path->remoteAddress << endl;
-
-    // ====== Update congestion window =======================================
-    (this->*ccFunctions.ccUpdateAfterRtxTimeout)(path);
-
-
-    // ====== Error Counter Handling =========================================
-    if (!state->zeroWindowProbing) {
-        state->errorCount++;
-        path->pathErrorCount++;
-        sctpEV3 << "RTX-Timeout: errorCount increased to "<<path->pathErrorCount<<"  state->errorCount="<<state->errorCount<<"\n";
-    }
-    if (state->errorCount >= (uint32)sctpMain->par("assocMaxRetrans")) {
-        /* error counter exceeded terminate the association -- create an SCTPC_EV_CLOSE event and send it to myself */
-
-        sctpEV3 << "process_TIMEOUT_RTX : ASSOC ERROR COUNTER EXCEEDED, closing ASSOC" << endl;
-        sendIndicationToApp(SCTP_I_CONN_LOST);
-        sendAbort();
-        sctpMain->removeAssociation(this);
-        return 0;
-
-    }
-    else {
-        if (path->pathErrorCount > (uint32)sctpMain->par("pathMaxRetrans")) {
-            bool notifyUlp = false;
-
-            sctpEV3 << "pathErrorCount exceeded\n";
-            if (path->activePath) {
-                /* tell the source */
-                notifyUlp = true;
-            }
-            path->activePath = false;
-            if (path->remoteAddress == state->getPrimaryPathIndex()) {
-                SCTPPathVariables* nextPath = getNextPath(path);
-                if (nextPath != NULL) {
-                    state->setPrimaryPath(nextPath);
-                }
-            }
-            sctpEV3 << "process_TIMEOUT_RTX(" << path->remoteAddress
-                      << ") : PATH ERROR COUNTER EXCEEDED, path status is INACTIVE" << endl;
-            if (allPathsInactive()) {
-                sctpEV3 << "process_TIMEOUT_RTX: ALL PATHS INACTIVE --> connection LOST!" << endl;
-                sendIndicationToApp(SCTP_I_CONN_LOST);
-                sendAbort();
-                sctpMain->removeAssociation(this);
-                return 0;
-            }
-            else if (notifyUlp) {
-                // Send notification to the application
-                pathStatusIndication(path, false);
-            }
-        }
-        sctpEV3 << "process_TIMEOUT_RTX(" << path->remoteAddress
-                  << ") : PATH ERROR COUNTER now " << path->pathErrorCount << endl;
-    }
-
-
-    // ====== Do Retransmission ==============================================
-    // dequeue all chunks not acked so far and put them in the TransmissionQ
-    if (!retransmissionQ->payloadQueue.empty()) {
-        sctpEV3 << "Still " << retransmissionQ->payloadQueue.size()
-                  << " chunks in retransmissionQ" << endl;
-
-        for (SCTPQueue::PayloadQueue::iterator iterator = retransmissionQ->payloadQueue.begin();
-            iterator != retransmissionQ->payloadQueue.end(); iterator++) {
-            SCTPDataVariables* chunk = iterator->second;
-            assert(chunk != NULL);
-
-            // ====== Insert chunks into TransmissionQ ============================
-            // Only insert chunks that were sent to the path that has timed out
-            if ( ((chunkHasBeenAcked(chunk) == false && chunk->countsAsOutstanding) || chunk->hasBeenReneged) &&
-                  (chunk->getLastDestinationPath() == path) ) {
-                sctpEV3 << simTime() << ": Timer-Based RTX for TSN "
-                          << chunk->tsn << " on path " << chunk->getLastDestination() << endl;
-                chunk->getLastDestinationPath()->numberOfTimerBasedRetransmissions++;
-                SCTP::AssocStatMap::iterator iter = sctpMain->assocStatMap.find(assocId);
-                iter->second.numT3Rtx++;
-
-                moveChunkToOtherPath(chunk, getNextDestination(chunk));
-            }
-        }
-    }
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
 
+   SCTPPathVariables* nextPath = getNextPath(path);
+   sctpEV3 << "TimeoutRTX: sendOnAllPaths()" << endl;
+   sendOnAllPaths(nextPath);
+
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+}
 
-    SCTPPathVariables* nextPath = getNextPath(path);
-    sctpEV3 << "TimeoutRTX: sendOnAllPaths()" << endl;
-    sendOnAllPaths(nextPath);
 
-    return 0;
+void SCTPAssociation::process_TIMEOUT_BLOCKING(SCTPPathVariables* path)
+{
 }
 
 
 void SCTPAssociation::moveChunkToOtherPath(SCTPDataVariables* chunk,
-                                                         SCTPPathVariables* newPath)
+                                           SCTPPathVariables* newPath)
 {
-    // ====== Prepare next destination =======================================
-    SCTPPathVariables* lastPath = chunk->getLastDestinationPath();
-    chunk->hasBeenFastRetransmitted = false;
-    chunk->gapReports                     = 0;
-    chunk->setNextDestination(newPath);
-    sctpEV3 << simTime() << ": Timer-Based RTX for TSN " << chunk->tsn
-              << ": lastDestination=" << chunk->getLastDestination()
-              << " nextDestination="  << chunk->getNextDestination() << endl;
-
-    // ======= Remove chunk's booksize from outstanding bytes ================
-    // T.D. 12.02.2010: This case may happen when using sender queue control!
-    if(chunk->countsAsOutstanding) {
-        assert(lastPath->outstandingBytes >= chunk->booksize);
-        lastPath->outstandingBytes -= chunk->booksize;
-        assert((int32)lastPath->outstandingBytes >= 0);
-        state->outstandingBytes -= chunk->booksize;
-        assert((int64)state->outstandingBytes >= 0);
-        chunk->countsAsOutstanding = false;
-        // T.D. 12.02.2010: No Timer-Based RTX is necessary any more when there
-        //                        are no outstanding bytes!
-        if(lastPath->outstandingBytes == 0) {
-            stopTimer(lastPath->T3_RtxTimer);
-        }
-    }
-
-
-    // ====== Perform bookkeeping ============================================
-    // Check, if chunk_ptr->tsn is already in transmission queue.
-    // This can happen in case multiple timeouts occur in succession.
-    if (!transmissionQ->checkAndInsertChunk(chunk->tsn, chunk)) {
-        sctpEV3 << "TSN " << chunk->tsn << " already in transmissionQ" << endl;
-        return;
-    }
-    else {
-        chunk->enqueuedInTransmissionQ = true;
-        sctpEV3 << "Inserting TSN " << chunk->tsn << " into transmissionQ" << endl;
-        CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination());
-        q->second += ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH);
-        CounterMap::iterator qb = qCounter.bookedTransQ.find(chunk->getNextDestination());
-        qb->second += chunk->booksize;
-
-        if (chunk->countsAsOutstanding) {
-            decreaseOutstandingBytes(chunk);
-        }
-        state->peerRwnd += (chunk->booksize);
-    }
-    if (state->peerRwnd > state->initialPeerRwnd) {
-        state->peerRwnd = state->initialPeerRwnd;
-    }
-    sctpEV3 << "T3 Timeout: Chunk (TSN=" << chunk->tsn
-                << ") has been requeued in transmissionQ, rwnd was set to "
-                << state->peerRwnd << endl;
+   // ======= Remove chunk from outstanding bytes ===========================
+   if (chunk->countsAsOutstanding) {
+      decreaseOutstandingBytes(chunk);
+   }
+
+   // ====== Prepare next destination =======================================
+   chunk->hasBeenFastRetransmitted = false;
+   chunk->gapReports               = 0;
+   chunk->setNextDestination(newPath);
+
+   // ====== Rebook chunk on new path =======================================
+   assert(chunk->queuedOnPath->queuedBytes >= chunk->booksize);
+   chunk->queuedOnPath->queuedBytes -= chunk->booksize;
+   chunk->queuedOnPath->statisticsPathQueuedSentBytes->record(chunk->queuedOnPath->queuedBytes);
+
+   chunk->queuedOnPath = chunk->getNextDestinationPath();
+   chunk->queuedOnPath->queuedBytes += chunk->booksize;
+   chunk->queuedOnPath->statisticsPathQueuedSentBytes->record(chunk->queuedOnPath->queuedBytes);
+
+   // ====== Perform bookkeeping ============================================
+   // Check, if chunk_ptr->tsn is already in transmission queue.
+   // This can happen in case multiple timeouts occur in succession.
+   if (!transmissionQ->checkAndInsertChunk(chunk->tsn, chunk)) {
+      sctpEV3 << "TSN " << chunk->tsn << " already in transmissionQ" << endl;
+      return;
+   }
+   else {
+      chunk->enqueuedInTransmissionQ = true;
+      sctpEV3 << "Inserting TSN " << chunk->tsn << " into transmissionQ" << endl;
+      CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination());
+      q->second += ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH);
+      CounterMap::iterator qb = qCounter.bookedTransQ.find(chunk->getNextDestination());
+      qb->second += chunk->booksize;
+      state->peerRwnd += (chunk->booksize + state->bytesToAddPerPeerChunk);
+      if (state->peerAllowsChunks) {
+         state->peerMsgRwnd++;
+      }
+   }
+   if (state->peerRwnd > state->initialPeerRwnd) {
+      state->peerRwnd = state->initialPeerRwnd;
+   }
+   if (state->peerAllowsChunks && state->peerMsgRwnd > state->initialPeerMsgRwnd) {
+      state->peerMsgRwnd = state->initialPeerMsgRwnd;
+   }
+
+   // T.D. 02.08.2011: The peer window may not be full anymore!
+   if ( (state->peerWindowFull) && (state->peerRwnd > 0) ) {
+      state->peerWindowFull = false;
+   }
+
+   statisticsPeerRwnd->record(state->peerRwnd);
 }
diff --git a/src/transport/sctp/SCTPAssociationSendAll.cc b/src/transport/sctp/SCTPAssociationSendAll.cc
index 5e64357..f4e0c8d 100644
--- a/src/transport/sctp/SCTPAssociationSendAll.cc
+++ b/src/transport/sctp/SCTPAssociationSendAll.cc
@@ -1,6 +1,6 @@
 //
 // Copyright (C) 2007-2009 Irene Ruengeler
-// Copyright (C) 2009-2010 Thomas Dreibholz
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -24,723 +24,1049 @@
 #include <assert.h>
 #include <algorithm>
 
+// ====== Activate debugging mode here! ===================
+// #define SNDMESSAGE_DEBUG
+// ========================================================
+
 
 void SCTPAssociation::increaseOutstandingBytes(SCTPDataVariables* chunk,
-                                                              SCTPPathVariables* path)
+                                               SCTPPathVariables* path)
 {
-    path->outstandingBytes += chunk->booksize;
-    state->outstandingBytes += chunk->booksize;
-
-    CounterMap::iterator iterator = qCounter.roomRetransQ.find(path->remoteAddress);
-        iterator->second += ADD_PADDING(chunk->booksize + SCTP_DATA_CHUNK_LENGTH);
+   path->outstandingBytes += chunk->booksize;
+   path->statisticsPathOutstandingBytes->record(path->outstandingBytes);
+   state->outstandingBytes += chunk->booksize;
+   statisticsOutstandingBytes->record(state->outstandingBytes);
+
+   CounterMap::iterator iterator = qCounter.roomRetransQ.find(path->remoteAddress);
+   state->outstandingMessages++;
+   if (state->osbWithHeader)
+      iterator->second += ADD_PADDING(chunk->booksize);
+   else
+      iterator->second += ADD_PADDING(chunk->booksize + SCTP_DATA_CHUNK_LENGTH);
 }
 
-int32 SCTPAssociation::calculateBytesToSendOnPath(const SCTPPathVariables* pathVar)
+void SCTPAssociation::storePacket(SCTPPathVariables* pathVar,
+                                  SCTPMessage*       sctpMsg,
+                                  const uint16       chunksAdded,
+                                  const uint16       dataChunksAdded,
+                                  const bool         authAdded)
 {
-    int32                    bytesToSend;
-    const SCTPDataMsg* datMsg = peekOutboundDataMsg();
-    if(datMsg != NULL) {
-        const uint32 ums = datMsg->getBooksize();        // Get user message size
-            const uint32 num = (uint32)floor((double)(pathVar->pmtu - 32) / (ums + SCTP_DATA_CHUNK_LENGTH));
-            if (num * ums > state->peerRwnd) {
-                // Receiver cannot handle data yet
-                bytesToSend = 0;
-            }
-            else {
-                // Receiver will accept data
-                bytesToSend = num * ums;
-            }
-    }
-    else {
-        bytesToSend = 0;
-    }
-    return(bytesToSend);
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+
+   uint32 packetBytes = 0;
+   for (uint16 i = 0; i < sctpMsg->getChunksArraySize(); i++) {
+      SCTPDataVariables* chunk = retransmissionQ->payloadQueue.find(((SCTPDataChunk*)sctpMsg->getChunks(i))->getTsn())->second;
+      decreaseOutstandingBytes(chunk);
+      chunk->queuedOnPath->queuedBytes -= chunk->booksize;
+      chunk->queuedOnPath = NULL;
+      packetBytes += chunk->booksize;
+   }
+   state->sctpMsg         = sctpMsg;
+   state->chunksAdded     = chunksAdded;
+   state->dataChunksAdded = dataChunksAdded;
+   state->packetBytes     = packetBytes;
+   state->authAdded       = authAdded;
+   sctpEV3 << "storePacket: path=" << pathVar->remoteAddress
+           << " state->packetBytes=" << state->packetBytes
+           << " osb=" << pathVar->outstandingBytes << " -> "
+           << pathVar->outstandingBytes - state->packetBytes << endl;
+   if (state->osbWithHeader)
+      qCounter.roomSumSendStreams += state->packetBytes;
+   else
+      qCounter.roomSumSendStreams += state->packetBytes + (dataChunksAdded * SCTP_DATA_CHUNK_LENGTH);
+   qCounter.bookedSumSendStreams += state->packetBytes;
+
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
 }
 
-void SCTPAssociation::storePacket(SCTPPathVariables* pathVar,
-                                             SCTPMessage*         sctpMsg,
-                                             const uint16         chunksAdded,
-                                             const uint16         dataChunksAdded,
-                                             const uint32         packetBytes,
-                                             const bool           authAdded)
+void SCTPAssociation::loadPacket(SCTPPathVariables* pathVar,
+                                 SCTPMessage**      sctpMsg,
+                                 uint16*            chunksAdded,
+                                 uint16*            dataChunksAdded,
+                                 bool*              authAdded)
 {
-    for (uint16 i = 0; i < sctpMsg->getChunksArraySize(); i++) {
-        retransmissionQ->payloadQueue.find(((SCTPDataChunk*)sctpMsg->getChunks(i))->getTsn())->second->countsAsOutstanding = false;
-    }
-    state->sctpMsg            = sctpMsg;
-    state->chunksAdded    = chunksAdded;
-    state->dataChunksAdded = dataChunksAdded;
-    state->packetBytes    = packetBytes;
-    sctpEV3 << "storePacket: path=" << pathVar->remoteAddress
-              << " osb=" << pathVar->outstandingBytes << " -> "
-              << pathVar->outstandingBytes - state->packetBytes << endl;
-    assert(pathVar->outstandingBytes >= state->packetBytes);
-    pathVar->outstandingBytes -= state->packetBytes;
-        qCounter.roomSumSendStreams += state->packetBytes + (dataChunksAdded * SCTP_DATA_CHUNK_LENGTH);
-    qCounter.bookedSumSendStreams += state->packetBytes;
-
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+
+   *sctpMsg         = state->sctpMsg;
+   state->sctpMsg   = NULL;
+   *chunksAdded     = state->chunksAdded;
+   *dataChunksAdded = state->dataChunksAdded;
+   *authAdded       = state->authAdded;
+   sctpEV3 << "loadPacket: path=" << pathVar->remoteAddress << " osb=" << pathVar->outstandingBytes << " -> " << pathVar->outstandingBytes + state->packetBytes << endl;
+   if (state->osbWithHeader) {
+      qCounter.roomSumSendStreams -= state->packetBytes;
+   }
+   else {
+      qCounter.roomSumSendStreams -= state->packetBytes + ((*dataChunksAdded) * SCTP_DATA_CHUNK_LENGTH);
+   }
+   qCounter.bookedSumSendStreams -= state->packetBytes;
+
+   for (uint16 i = 0; i < (*sctpMsg)->getChunksArraySize(); i++) {
+      SCTPDataVariables* chunk = retransmissionQ->payloadQueue.find(((SCTPDataChunk*)(*sctpMsg)->getChunks(i))->getTsn())->second;
+      chunk->queuedOnPath = pathVar;
+      chunk->queuedOnPath->queuedBytes += chunk->booksize;
+      chunk->setLastDestination(pathVar);
+      increaseOutstandingBytes(chunk, pathVar);
+      chunk->countsAsOutstanding = true;
+   }
+
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
 }
 
-void SCTPAssociation::loadPacket(SCTPPathVariables* pathVar,
-                                            SCTPMessage**        sctpMsg,
-                                            uint16*              chunksAdded,
-                                            uint16*              dataChunksAdded,
-                                            uint32*              packetBytes,
-                                            bool*                    authAdded)
+
+void SCTPAssociation::dumpQueue(std::ostream& os)
 {
-    *sctpMsg = state->sctpMsg;
-    *chunksAdded = state->chunksAdded;
-    *dataChunksAdded = state->dataChunksAdded;
-    *packetBytes = state->packetBytes;
-    sctpEV3 << "loadPacket: path=" << pathVar->remoteAddress << " osb=" << pathVar->outstandingBytes << " -> " << pathVar->outstandingBytes + state->packetBytes << endl;
-    pathVar->outstandingBytes += state->packetBytes;
-    qCounter.bookedSumSendStreams -= state->packetBytes;
+/*
+   os << "sendAll: Output Queue Dump:" << endl;
+   for (SCTPSendStreamMap::iterator iterator = sendStreams.begin(); iterator != sendStreams.end(); ++iterator) {
+      SCTPSendStream* stream = iterator->second;
+
+      SCTPDataMsgQueue* orderedStreamQ   = stream->getStreamQ();
+      SCTPDataMsgQueue* unorderedStreamQ = stream->getUnorderedStreamQ();
+
+      os << " + Stream " << (int32)iterator->first
+         << " (" << orderedStreamQ->getLength() << " ordered, " << unorderedStreamQ->getLength() << " unordered packets)"
+         << endl;
+
+      for(cQueue::Iterator packetIterator(*orderedStreamQ); !packetIterator.end(); packetIterator++) {
+         const SCTPDataMsg* datMsg = (const SCTPDataMsg*)packetIterator();
+         os << "   - #"         << datMsg->getMsgNum()
+            << ", length "      << datMsg->getByteLength()
+            << ", destination " << datMsg->getInitialDestination()
+            << endl;
+      }
+   }
+*/
+   os << "retransmissionQ:" << endl;
+   for(SCTPQueue::PayloadQueue::const_iterator iterator = retransmissionQ->payloadQueue.begin();
+      iterator != retransmissionQ->payloadQueue.end(); ++iterator) {
+      const SCTPDataVariables* chunk = iterator->second;
+      const SCTPPathVariables* path  = chunk->getLastDestinationPath();
+
+      os << "   - #"      << chunk->tsn
+         << "\tbooksize=" << chunk->booksize
+         << "\tpath="     << ((path != NULL) ? path->remoteAddress.str().c_str() : "")
+         << "\tsentTime=" << simTime() - chunk->sendTime
+         << "\trtx="      << chunk->numberOfRetransmissions
+         << ((chunk->countsAsOutstanding == true)      ? " OUTSTANDING" : "")
+         << ((chunk->hasBeenFastRetransmitted == true) ? " FastRTX'ed"  : "")
+         << ((chunk->hasBeenMoved == true) ? " MOVED" : "")
+         << ((chunk->hasBeenAcked == true) ? " ACKED" : "")
+         << endl;
+   }
+}
 
-    for (uint16 i = 0; i < (*sctpMsg)->getChunksArraySize(); i++)
-        retransmissionQ->payloadQueue.find(((SCTPDataChunk*)(*sctpMsg)->getChunks(i))->getTsn())->second->countsAsOutstanding = true;
 
+void SCTPAssociation::dumpPaths(std::ostream& os) const
+{
+   os << "Path Dump at t=" << simTime() << ":" << endl;
+   for (SCTPPathMap::const_iterator iterator = sctpPathMap.begin(); iterator != sctpPathMap.end(); ++iterator) {
+      const SCTPPathVariables* path = iterator->second;
+      os << " - "                    << path->remoteAddress
+         << "\tcwnd="                << path->cwnd
+         << "\tssthresh="            << path->ssthresh
+         << "\toutstanding="         << path->outstandingBytes
+         << "\tsrtt="                << path->srtt
+         << "\trto="                 << path->pathRto
+         << "\tFR="                  << ((path->fastRecoveryActive == true) ? "YES!" : "no")
+         << "\tFRexit="              << path->fastRecoveryExitPoint
+         ;
+      const uint32 room   = qCounter.roomTransQ.find(path->remoteAddress)->second;
+      const uint32 booked = qCounter.bookedTransQ.find(path->remoteAddress)->second;
+      os << "\troomTransQ="   << room
+         << "\tbookedTransQ=" << booked
+         << endl;
+   }
 }
 
 
 
-SCTPDataVariables* SCTPAssociation::makeDataVarFromDataMsg(SCTPDataMsg*         datMsg,
-                                                                              SCTPPathVariables* path)
+
+SCTPDataVariables* SCTPAssociation::makeDataVarFromDataMsg(SCTPDataMsg*       datMsg,
+                                                           SCTPPathVariables* path)
 {
-    SCTPDataVariables* datVar = new SCTPDataVariables();
-
-    datMsg->setInitialDestination(path->remoteAddress);
-    datVar->setInitialDestination(path);
-
-    datVar->bbit                            = datMsg->getBBit();
-    datVar->ebit                            = datMsg->getEBit();
-    datVar->enqueuingTime               = datMsg->getEnqueuingTime();
-    datVar->expiryTime                  = datMsg->getExpiryTime();
-    datVar->ppid                            = datMsg->getPpid();
-    datVar->len                             = datMsg->getBitLength();
-    datVar->sid                             = datMsg->getSid();
-    datVar->allowedNoRetransmissions = datMsg->getRtx();
-    datVar->booksize                        = datMsg->getBooksize();
-
-    // ------ Stream handling ---------------------------------------
-    SCTPSendStreamMap::iterator iterator = sendStreams.find(datMsg->getSid());
-    SCTPSendStream*              stream  = iterator->second;
-    uint32                           nextSSN     = stream->getNextStreamSeqNum();
-    datVar->userData = datMsg->decapsulate();
-    if (datMsg->getOrdered()) {
-        // ------ Ordered mode: assign SSN ---------
-        if (datMsg->getEBit())
-        {
-            datVar->ssn = nextSSN++;
-        }
-        else
-        {
-            datVar->ssn = nextSSN;
-        }
-
-        datVar->ordered = true;
-        if (nextSSN > 65535) {
-            stream->setNextStreamSeqNum(0);
-        }
-        else {
-            stream->setNextStreamSeqNum(nextSSN);
-        }
-    }
-    else {
-        // ------ Ordered mode: no SSN needed ------
-        datVar->ssn      = 0;
-        datVar->ordered = false;
-    }
-
-
-    return(datVar);
+   SCTPDataVariables* datVar = new SCTPDataVariables();
+
+   datMsg->setInitialDestination(path->remoteAddress);
+   datVar->setInitialDestination(path);
+
+   datVar->bbit                     = datMsg->getBBit();
+   datVar->ebit                     = datMsg->getEBit();
+   datVar->ibit                     = datMsg->getSackNow();
+   datVar->enqueuingTime            = datMsg->getEnqueuingTime();
+   datVar->expiryTime               = datMsg->getExpiryTime();
+   datVar->ppid                     = datMsg->getPpid();
+   datVar->len                      = datMsg->getBitLength();
+   datVar->sid                      = datMsg->getSid();
+   datVar->allowedNoRetransmissions = datMsg->getRtx();
+   datVar->booksize                 = datMsg->getBooksize();
+   datVar->prMethod                 = datMsg->getPrMethod();
+   datVar->priority                 = datMsg->getPriority();
+   datVar->strReset                 = datMsg->getStrReset();
+
+   // ------ Stream handling ---------------------------------------
+   SCTPSendStreamMap::iterator iterator = sendStreams.find(datMsg->getSid());
+   SCTPSendStream*             stream   = iterator->second;
+   uint32                      nextSSN  = stream->getNextStreamSeqNum();
+   datVar->userData = datMsg->decapsulate();
+   if (datMsg->getOrdered()) {
+      // ------ Ordered mode: assign SSN ---------
+      if (datMsg->getEBit())
+      {
+         datVar->ssn = nextSSN++;
+      }
+      else
+      {
+         datVar->ssn = nextSSN;
+      }
+
+      datVar->ordered = true;
+      if (nextSSN > 65535) {
+         stream->setNextStreamSeqNum(0);
+      }
+      else {
+         stream->setNextStreamSeqNum(nextSSN);
+      }
+   }
+   else {
+      // ------ Ordered mode: no SSN needed ------
+      datVar->ssn     = 0;
+      datVar->ordered = false;
+   }
+
+   state->ssLastDataChunkSizeSet = true;
+
+   return(datVar);
 }
 
 SCTPPathVariables* SCTPAssociation::choosePathForRetransmission()
 {
-    uint32               max    = 0;
-    SCTPPathVariables* temp = NULL;
-
-    for (SCTPPathMap::iterator iterator = sctpPathMap.begin(); iterator != sctpPathMap.end(); ++iterator) {
-        SCTPPathVariables*          path = iterator->second;
-        CounterMap::const_iterator tq     = qCounter.roomTransQ.find(path->remoteAddress);
-        if ((tq != qCounter.roomTransQ.end()) && (tq->second > max)) {
-            max = tq->second;
-            temp = path;
-        }
-    }
-    return temp;
+   uint32             max  = 0;
+   SCTPPathVariables* temp = NULL;
+
+   for (SCTPPathMap::iterator iterator = sctpPathMap.begin(); iterator != sctpPathMap.end(); ++iterator) {
+      SCTPPathVariables*         path = iterator->second;
+      CounterMap::const_iterator tq   = qCounter.roomTransQ.find(path->remoteAddress);
+      if ((tq != qCounter.roomTransQ.end()) && (tq->second > max)) {
+         max = tq->second;
+         temp = path;
+      }
+   }
+   return temp;
 }
 
 void SCTPAssociation::timeForSack(bool& sackOnly, bool& sackWithData)
 {
-    sackOnly = sackWithData = false;
-        if (((state->numGaps > 0) || (state->dupList.size() > 0)) &&
-             (state->sackAllowed)) {
-        // Schedule sending of SACKs at once, when we have fragments to report
-        state->ackState = sackFrequency;
-        sackOnly = sackWithData = true;  // SACK necessary, regardless of data available
-    }
-    if (state->ackState >= sackFrequency) {
-        sackOnly = sackWithData = true;  // SACK necessary, regardless of data available
-    }
-    else if (SackTimer->isScheduled()) {
-        sackOnly         = false;
-        sackWithData = true;      // Only send SACK when data is present.
-    }
+   sackOnly = sackWithData = false;
+   if ( ((state->gapList.getNumGaps(GapList::GT_Any) > 0) || (state->dupList.size() > 0)) &&
+          (state->sackAllowed) ) {
+      // Schedule sending of SACKs at once, when we have fragments to report
+      state->ackState = sackFrequency;
+      sackOnly = sackWithData = true;   // SACK necessary, regardless of data available
+   }
+   if (state->ackState >= sackFrequency) {
+      sackOnly = sackWithData = true;   // SACK necessary, regardless of data available
+   }
+   else if (SackTimer->isScheduled()) {
+      sackOnly     = false;
+      sackWithData = true;   // Only send SACK when data is present.
+   }
 }
 
 
 void SCTPAssociation::sendOnAllPaths(SCTPPathVariables* firstPath)
 {
-        // ------ Send on provided path first ... -----------------------------
-        if(firstPath != NULL) {
-            sendOnPath(firstPath);
-        }
-
-        // ------ ... then, try sending on all other paths --------------------
-        for (SCTPPathMap::iterator iterator = sctpPathMap.begin(); iterator != sctpPathMap.end(); ++iterator) {
-            SCTPPathVariables* path = iterator->second;
-            if(path != firstPath) {
-                sendOnPath(path);
-            }
-        }
+      // ------ Send on provided path first ... -----------------------------
+      if(firstPath != NULL) {
+         sendOnPath(firstPath);
+      }
+
+      // ------ ... then, try sending on all other paths --------------------
+      for (SCTPPathMap::iterator iterator = sctpPathMap.begin(); iterator != sctpPathMap.end(); ++iterator) {
+         SCTPPathVariables* path = iterator->second;
+         if(path != firstPath) {
+            sendOnPath(path);
+         }
+      }
 
 }
 
 
 
 
+void SCTPAssociation::sendSACKviaSelectedPath(SCTPMessage* sctpMsg)
+{
+   SCTPPathVariables* sackPath =
+      (state->lastDataSourceList.size() > 0) ? state->lastDataSourceList.front() :
+                                               state->lastDataSourcePath;
+   assert(sackPath != NULL);
+
+
+   sctpEV3 << assocId << ": sending SACK to " << sackPath->remoteAddress << endl;
+   sendToIP(sctpMsg, sackPath->remoteAddress);
+   sackPath->lastSACKSent = simTime();
+   state->lastDataSourceList.clear();   // Clear the address list!
+   recordTransmission(sctpMsg, sackPath);
+}
+
+
 void SCTPAssociation::bytesAllowedToSend(SCTPPathVariables* path,
-                                                      const bool            firstPass)
+                                         const bool         firstPass)
 {
-    assert(path != NULL);
-
-    bytes.chunk         = false;
-    bytes.packet        = false;
-    bytes.bytesToSend = 0;
-
-    sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "):"
-              << " osb=" << path->outstandingBytes << " cwnd=" << path->cwnd << endl;
-
-    // ====== First transmission =============================================
-    if (!state->firstDataSent) {
-        bytes.chunk = true;
-    }
-
-    // ====== Transmission allowed by peer's receiver window? ================
-    else if (state->peerWindowFull)
-    {
-        if (path->outstandingBytes == 0) {
-            // Zero Window Probing
-            sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): zeroWindowProbing" << endl;
-            state->zeroWindowProbing = true;
-            bytes.chunk = true;
-        }
-    }
-
-    // ====== Retransmissions ================================================
-    else {
-        CounterMap::const_iterator it = qCounter.roomTransQ.find(path->remoteAddress);
-        sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): bytes in transQ=" << it->second << endl;
-        if (it->second > 0) {
-            const int32 allowance = path->cwnd - path->outstandingBytes;
-            sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): cwnd-osb=" << allowance << endl;
-            if (state->peerRwnd < path->pmtu) {
-                bytes.bytesToSend = state->peerRwnd;
-                sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): rwnd<pmtu" << endl;
-                return;
-            }
-            else if (allowance > 0) {
-                CounterMap::const_iterator bit = qCounter.bookedTransQ.find(path->remoteAddress);
-                if (bit->second > (uint32)allowance) {
-                    bytes.bytesToSend = allowance;
-                    sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): cwnd does not allow all RTX" << endl;
-                    return;  // More bytes available than allowed -> just return what is allowed.
-                }
-                else {
-                    bytes.bytesToSend = bit->second;
-                    sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): cwnd allows more than those "
-                                 << bytes.bytesToSend << " bytes for retransmission" << endl;
-                }
+   assert(path != NULL);
+
+   bytes.chunk       = false;
+   bytes.packet      = false;
+   bytes.bytesToSend = 0;
+
+   sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "):"
+           << " osb=" << path->outstandingBytes << " cwnd=" << path->cwnd << endl;
+
+   // ====== First transmission =============================================
+   if (!state->firstDataSent) {
+      bytes.chunk = true;
+   }
+
+   // ====== Transmission allowed by peer's receiver window? ================
+   else if (state->peerWindowFull || (state->peerAllowsChunks && state->peerMsgRwnd <= 0)) {
+      if (path->outstandingBytes == 0) {
+         // Zero Window Probing
+         sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): zeroWindowProbing" << endl;
+         state->zeroWindowProbing = true;
+         bytes.chunk = true;
+      }
+   }
+
+   // ====== Retransmissions ================================================
+   else {
+      CounterMap::const_iterator it = qCounter.roomTransQ.find(path->remoteAddress);
+      sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): bytes in transQ=" << it->second << endl;
+      if (it->second > 0) {
+         const int32 allowance = path->cwnd - path->outstandingBytes;
+         sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): cwnd-osb=" << allowance << endl;
+         if (state->peerRwnd < path->pmtu) {
+            bytes.bytesToSend = state->peerRwnd;
+            sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): rwnd<pmtu" << endl;
+            return;
+         }
+         else if (allowance > 0) {
+            CounterMap::const_iterator bit = qCounter.bookedTransQ.find(path->remoteAddress);
+            if (bit->second > (uint32)allowance) {
+               bytes.bytesToSend = allowance;
+               sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): cwnd does not allow all RTX" << endl;
+               return;   // More bytes available than allowed -> just return what is allowed.
             }
-            else {  // You may retransmit one packet
-                bytes.packet = true;
-                sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): allowance<=0: retransmit one packet" << endl;
+            else {
+               bytes.bytesToSend = bit->second;
+               sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): cwnd allows more than those "
+                         << bytes.bytesToSend << " bytes for retransmission" << endl;
             }
-        }
-
-        // ====== New transmissions ===========================================
-        if (!bytes.chunk && !bytes.packet) {
-            if ((path->outstandingBytes < path->cwnd) &&
-                 (!state->peerWindowFull)) {
-                sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "):"
-                          << " bookedSumSendStreams=" << qCounter.bookedSumSendStreams
-                          << " bytes.bytesToSend="      << bytes.bytesToSend << endl;
-                const int32 allowance = path->cwnd - path->outstandingBytes - bytes.bytesToSend;
-                if (allowance > 0) {
-                    if (qCounter.bookedSumSendStreams > (uint32)allowance) {
-                        bytes.bytesToSend = path->cwnd - path->outstandingBytes;
-                        sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): bytesToSend are limited by cwnd: "
-                                  << bytes.bytesToSend << endl;
-                    }
-                    else {
-                        bytes.bytesToSend += qCounter.bookedSumSendStreams;
-                        sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): send all stored bytes: "
-                                  << bytes.bytesToSend << endl;
-                    }
-                }
+         }
+         else {   // You may retransmit one packet
+            bytes.packet = true;
+            sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): allowance<=0: retransmit one packet" << endl;
+         }
+      }
+
+      // ====== New transmissions ===========================================
+      if (!bytes.chunk && !bytes.packet) {
+         (this->*ccFunctions.ccUpdateMaxBurst)(path);
+
+         // ====== Get cwnd value to use, according to maxBurstVariant ======
+         uint32 myCwnd = path->cwnd;
+
+         // ====== Obtain byte allowance ====================================
+         if ( ( ((state->peerAllowsChunks) &&
+                 (path->outstandingBytes < myCwnd) &&
+                 (!state->peerWindowFull) &&
+                 (state->peerMsgRwnd > 0))
+                 ||
+                ((!state->peerAllowsChunks) &&
+                 (path->outstandingBytes < myCwnd) &&
+                 (!state->peerWindowFull)) )
+            ) {
+            sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "):"
+                    << " bookedSumSendStreams=" << qCounter.bookedSumSendStreams
+                    << " bytes.bytesToSend="    << bytes.bytesToSend << endl;
+            const int32 allowance = myCwnd - path->outstandingBytes - bytes.bytesToSend;
+            if (allowance > 0) {
+               if (qCounter.bookedSumSendStreams > (uint32)allowance) {
+                  bytes.bytesToSend = myCwnd - path->outstandingBytes;
+                  sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): bytesToSend are limited by cwnd: "
+                          << bytes.bytesToSend << endl;
+               }
+               else {
+                  bytes.bytesToSend += qCounter.bookedSumSendStreams;
+                  sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "): send all stored bytes: "
+                          << bytes.bytesToSend << endl;
+               }
             }
-        }
-    }
-
-    sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "):"
-              << " osb="  << path->outstandingBytes
-              << " cwnd=" << path->cwnd
-              << " bytes.packet=" << (bytes.packet ? "YES!" : "no")
-              << " bytes.chunk="     << (bytes.chunk    ? "YES!" : "no")
-              << " bytes.bytesToSend=" << bytes.bytesToSend << endl;
+         }
+      }
+   }
+
+   sctpEV3 << "bytesAllowedToSend(" << path->remoteAddress << "):"
+           << " osb="  << path->outstandingBytes
+           << " cwnd=" << path->cwnd
+           << " bytes.packet=" << (bytes.packet ? "YES!" : "no")
+           << " bytes.chunk="  << (bytes.chunk  ? "YES!" : "no")
+           << " bytes.bytesToSend=" << bytes.bytesToSend << endl;
 }
 
 
 void SCTPAssociation::sendOnPath(SCTPPathVariables* pathId, bool firstPass)
 {
-    // ====== Variables ======================================================
-    SCTPPathVariables*  path             = NULL;      // Path to send next message to
-    SCTPMessage*            sctpMsg      = NULL;
-    SCTPSackChunk*          sackChunk    = NULL;
-    SCTPDataChunk*          chunkPtr         = NULL;
-
-    uint16 chunksAdded      = 0;
-    uint16 dataChunksAdded  = 0;
-    uint32 totalChunksSent  = 0;
-    uint32 totalPacketsSent = 0;
-    uint32 packetBytes      = 0;
-    uint32 outstandingBytes = 0;
-
-    uint32 tcount               = 0;     // Bytes in transmission queue on the selected path
-    uint32 scount               = 0;     // Bytes in send streams
-    int32 bytesToSend           = 0;
-
-    bool headerCreated      = false;
-    bool rtxActive              = false;
-    bool sendOneMorePacket  = false;
-    bool sendingAllowed     = true;
-    bool authAdded              = false;
-    bool sackAdded              = false;
-
-    // ====== Obtain path ====================================================
-    sctpEV3 << endl << "##### sendAll(";
-    if(pathId) {
-        sctpEV3 << pathId->remoteAddress;
-    }
-    sctpEV3 << ") #####" << endl;
-    while(sendingAllowed)
-    {
-        headerCreated = false;
-        if (state->bytesToRetransmit > 0) {
-            // There are bytes in the transmissionQ. They have to be sent first.
-            path = choosePathForRetransmission();
-            assert(path != NULL);
-            rtxActive = true;
-        }
-        else {
-            if (pathId == NULL) {   // No path given => use primary path.
-                path = state->getPrimaryPath();
-            }
-            else {
-                path = pathId;
+   // ====== Variables ======================================================
+   SCTPPathVariables*   path         = NULL;   // Path to send next message to
+   SCTPMessage*         sctpMsg      = NULL;
+   SCTPSackChunk*       sackChunk    = NULL;
+   SCTPDataChunk*       chunkPtr     = NULL;
+   SCTPDataVariables*   datVar       = NULL;
+   SCTPForwardTsnChunk* forwardChunk = NULL;
+
+   uint16 chunksAdded      = 0;
+   uint16 dataChunksAdded  = 0;
+   uint32 totalChunksSent  = 0;
+   uint32 totalPacketsSent = 0;
+   uint32 outstandingBytes = 0;
+
+   uint32 tcount           = 0;   // Bytes in transmission queue on the selected path
+   uint32 Tcount           = 0;   // Bytes in transmission queue on all paths
+   uint32 scount           = 0;   // Bytes in send streams
+   int32 bytesToSend       = 0;
+
+   bool headerCreated      = false;
+   bool sendOneMorePacket  = false;
+   bool sendingAllowed     = true;
+   bool authAdded          = false;
+   bool sackAdded          = false;
+   bool forwardPresent     = false;
+
+
+   // ====== Obtain path ====================================================
+   sctpEV3 << endl << "##### sendAll(";
+   if(pathId) {
+      sctpEV3 << pathId->remoteAddress;
+   }
+   sctpEV3 << ") at t=" << simTime() << " #####" << endl;
+   while(sendingAllowed)
+   {
+      headerCreated = false;
+      if (state->bytesToRetransmit > 0) {
+         // There are bytes in the transmissionQ. They have to be sent first.
+         path = choosePathForRetransmission();
+         assert(path != NULL);
+      }
+      else {
+         if (pathId == NULL) {   // No path given => use primary path.
+            path = state->getPrimaryPath();
+         }
+         else {
+            path = pathId;
+         }
+      }
+
+
+      outstandingBytes = path->outstandingBytes;
+      assert((int32)outstandingBytes >= 0);
+      CounterMap::iterator tq = qCounter.roomTransQ.find(path->remoteAddress);
+      tcount = tq->second;
+      Tcount = getAllTransQ();
+      scount = qCounter.roomSumSendStreams;    // includes header and padding
+      sctpEV3 << "\n"<<assocId<<": sendAll: on " << path->remoteAddress << ":"
+              << " tcount="       << tcount
+              << " Tcount="       << Tcount
+              << " scount="       << scount
+              << " nextTSN="      << state->nextTSN << endl;
+
+      if(SCTP::checkQueues) {    // Verbose status printing
+         dumpQueue();
+         dumpPaths();
+         checkOutstandingBytes();
+      }
+      bool sackOnly;
+      bool sackWithData;
+      timeForSack(sackOnly, sackWithData);
+
+      if ((tcount == 0 && scount == 0)
+         ) {
+         // ====== No DATA chunks to send or retransmissions due on another path ======
+         sctpEV3 << "No DATA chunk available!" << endl;
+         if (!sackOnly) {   // SACK?, no data to send
+            sctpEV3 << "No SACK to send either" << endl;
+            return;
+         }
+         else {
+            bytes.bytesToSend = 0;
+         }
+      }
+      else {
+         bytesAllowedToSend(path, firstPass);
+      }
+      bytesToSend = bytes.bytesToSend;
+      // As there is at least a SACK to be sent, a header can be created
+
+      if (state->sctpMsg) {
+         sctpEV3 << "packet was stored -> load packet" << endl;
+         loadPacket(path, &sctpMsg, &chunksAdded, &dataChunksAdded, &authAdded);
+         headerCreated = true;
+      }
+      else if (bytesToSend > 0 || bytes.chunk || bytes.packet || sackWithData || sackOnly || forwardPresent) {
+         sctpMsg = new SCTPMessage("send");
+         sctpMsg->setByteLength(SCTP_COMMON_HEADER);
+         headerCreated = true;
+         chunksAdded   = 0;
+      }
+
+
+      if (sackWithData || sackOnly)
+      {
+         // SACK can be sent
+         assert(headerCreated==true);
+         sackChunk = createSack();
+         chunksAdded++;
+         totalChunksSent++;
+         // ------ Create AUTH chunk, if necessary --------------------------
+         authAdded = addAuthChunkIfNecessary(sctpMsg, SACK, authAdded);
+
+         // ------ Add SACK chunk -------------------------------------------
+         sctpMsg->addChunk(sackChunk);
+         sackAdded = true;
+         sctpEV3<<assocId<<": SACK added, chunksAdded now "<<chunksAdded<<" sackOnly="<<sackOnly<<" sackWithData="<<sackWithData<<"\n";
+         if (sackOnly && !(bytesToSend > 0 || bytes.chunk || bytes.packet))
+         {
+            // There is no data to be sent, just the SACK
+            state->lastTransmission = simTime();
+            state->packetsInTotalBurst++;
+            if (dataChunksAdded > 0) {
+               state->ssNextStream = true;
             }
-        }
-
-        outstandingBytes = path->outstandingBytes;
-        assert((int32)outstandingBytes >= 0);
-        CounterMap::iterator tq = qCounter.roomTransQ.find(path->remoteAddress);
-        tcount = tq->second;
-        scount = qCounter.roomSumSendStreams;     // includes header and padding
-        sctpEV3 << "\nsendAll: on " << path->remoteAddress << ":"
-                  << " tcount="      << tcount
-                  << " scount="      << scount
-                  << " nextTSN="         << state->nextTSN << endl;
-
-        bool sackOnly;
-        bool sackWithData;
-        timeForSack(sackOnly, sackWithData);
-        if (tcount == 0 && scount == 0) {
-            // ====== No DATA chunks to send ===================================
-            sctpEV3 << "No DATA chunk available!" << endl;
-            if (!sackOnly) {     // SACK?, no data to send
-                sctpEV3 << "No SACK to send either" << endl;
-                return;
-            }
-            else {
-                bytes.bytesToSend = 0;
-            }
-        }
-        else {
-            bytesAllowedToSend(path, firstPass);
-        }
-        bytesToSend = bytes.bytesToSend;
-
-        // As there is at least a SACK to be sent, a header can be created
-
-        if (state->sctpMsg)  // ??? Robin: Ist das in Ordnung???
-        {
-            loadPacket(path, &sctpMsg, &chunksAdded, &dataChunksAdded, &packetBytes, &authAdded);
-            headerCreated = true;
-        }
-        else if (bytesToSend > 0 || bytes.chunk || bytes.packet || sackWithData || sackOnly) {
-            sctpMsg = new SCTPMessage("send");
-            //printf("%d Name=%s Pointer=%p\n", __LINE__, sctpMsg->getName(), sctpMsg);
-            sctpMsg->setByteLength(SCTP_COMMON_HEADER);
-            packetBytes = 0;
-            headerCreated = true;
-        }
-
-
-        if (sackWithData || sackOnly)
-        {
-            // SACK can be sent
-            assert(headerCreated==true);
-            sackChunk = createSack();
+            state->ackState = 0;
+            // Stop SACK timer if it is running...
+            stopTimer(SackTimer);
+            sctpAlgorithm->sackSent();
+            state->sackAllowed = false;
+            sendSACKviaSelectedPath(sctpMsg);
+            return;
+         }
+      }
+      // ====== FORWARD_TSN =================================================
+      if (!forwardPresent && !state->stopSending)
+      {
+         if (peekAbandonedChunk(path) != NULL)
+         {
+            forwardChunk = createForwardTsnChunk(path->remoteAddress);
             chunksAdded++;
             totalChunksSent++;
-            // ------ Create AUTH chunk, if necessary --------------------------
-
-            // ------ Add SACK chunk -------------------------------------------
-            sctpMsg->addChunk(sackChunk);
-            sackAdded = true;
-            if (sackOnly)     // ????? Robin: SACK mit FORWARD_TSN????
-            {
-                // send the packet and leave
-                //printf("%d Name=%s Pointer=%p, sctpMsg\n", __LINE__, sctpMsg->getName(), sctpMsg);
-                state->ackState = 0;
-                // Stop SACK timer if it is running...
-                stopTimer(SackTimer);
-                sctpAlgorithm->sackSent();
-                state->sackAllowed = false;
-                sendToIP(sctpMsg, state->lastDataSourceAddress);
-                if ((bytesToSend > 0) || (bytes.chunk) || (bytes.packet)) {
-                    sctpMsg = new SCTPMessage("send");
-                    sctpMsg->setByteLength(SCTP_COMMON_HEADER);
-                    packetBytes = 0;
-                    headerCreated = true;
-                    sackAdded = false;
-                }
-                else
-                    return;
+            state->ackPointAdvanced = false;
+            if (!headerCreated) {
+                sctpMsg = new SCTPMessage("send");
+                sctpMsg->setByteLength(SCTP_COMMON_HEADER);
+                headerCreated = true;
+                chunksAdded   = 0;
             }
-        }
-
+            // ------ Create AUTH chunk, if necessary -----------------------
+            authAdded = addAuthChunkIfNecessary(sctpMsg, FORWARD_TSN, authAdded);
+            sctpMsg->addChunk(forwardChunk);
+            forwardPresent = true;
+            if (!path->T3_RtxTimer->isScheduled()) {
+               // Start retransmission timer, if not scheduled before
+               startTimer(path->T3_RtxTimer, path->pathRto);
+            }
+         }
+      }
 
-        // ####################################################################
-        // #### Data Transmission                                                        ####
-        // ####################################################################
 
-        bool packetFull = false;
+      // ####################################################################
+      // #### Data Transmission                                          ####
+      // ####################################################################
 
-        while(!packetFull && headerCreated) {
-            assert(headerCreated == true);
-            sctpEV3 << "bytesToSend="    << bytesToSend
-                      << " bytes.chunk="     << bytes.chunk
-                      << " bytes.packet=" << bytes.packet << endl;
+      bool packetFull = false;
 
-            // ====== How many bytes may be transmitted in next packet? ========
-            int32 allowance = path->pmtu;     // Default behaviour: send 1 path MTU
-                if ((bytesToSend > 0) || (bytes.chunk) || (bytes.packet)) {
-                    // Allow 1 more MTU
-                }
-                else {
-                    // No more sending allowed.
-                    allowance   = 0;
-                    bytesToSend = 0;
-                }
+      while(!packetFull && headerCreated) {
+         assert(headerCreated == true);
+         sctpEV3<<assocId<<": bytesToSend="   << bytesToSend
+                 << " bytes.chunk="  << bytes.chunk
+                 << " bytes.packet=" << bytes.packet << endl;
 
-            if ((allowance > 0) || (bytes.chunk) || (bytes.packet)) {
-                bool firstTime = false;   // Is DATA chunk send for the first time?
+         // ====== How many bytes may be transmitted in next packet? ========
+         int32 allowance = path->pmtu;   // Default behaviour: send 1 path MTU
+            if ((bytesToSend > 0) || (bytes.chunk) || (bytes.packet)) {
+               // Allow 1 more MTU
+            }
+            else {
+               // No more sending allowed.
+               allowance   = 0;
+               bytesToSend = 0;
+            }
 
+         if ((allowance > 0) || (bytes.chunk) || (bytes.packet)) {
+            bool firstTime = false;   // Is DATA chunk send for the first time?
+            // ------ Create AUTH chunk, if necessary -----------------------
+            authAdded = addAuthChunkIfNecessary(sctpMsg, DATA, authAdded);
+            if (tcount > 0)  {
                 // ====== Retransmission ========================================
                 // T.D. 05.01.2010: If bytes.packet is true, one packet is allowed
-                //                        to be retransmitted!
-                SCTPDataVariables* datVar = getOutboundDataChunk(path,
-                                                                                 path->pmtu - sctpMsg->getByteLength() - 20,
-                                                                                 (bytes.packet == true) ? path->pmtu : allowance);
-                if (chunksAdded==1 && sackAdded && !sackOnly && datVar==NULL) {
-                    sctpMsg->removeChunk();
-                    delete sackChunk;
-                    datVar = getOutboundDataChunk(path,
-                                                            path->pmtu - sctpMsg->getByteLength() - 20,
-                                                            (bytes.packet == true) ? path->pmtu : allowance);
+                //                  to be retransmitted!
+                datVar = getOutboundDataChunk(path,
+                                              path->pmtu - sctpMsg->getByteLength() - 20,
+                                              (bytes.packet == true) ? path->pmtu : allowance);
+                if (datVar == NULL) {
+                   if (chunksAdded==1 && sackAdded) {
+                      datVar = getOutboundDataChunk(path,
+                                                    path->pmtu - sctpMsg->getByteLength() + sackChunk->getByteLength() - 20,
+                                                    (bytes.packet == true) ? path->pmtu : allowance);
+                      if (!sackOnly)  {
+                         sctpMsg->removeChunk();
+                         sctpEV3<<"RTX: Remove SACK chunk\n";
+                         delete sackChunk;
+                         chunksAdded--;
+                         sackAdded = false;
+                      }
+                      else {
+                         state->lastTransmission = simTime();
+                         state->packetsInTotalBurst++;
+                         if (dataChunksAdded > 0) {
+                            state->ssNextStream = true;
+                         }
+                         state->ackState = 0;
+                         // Stop SACK timer if it is running...
+                         stopTimer(SackTimer);
+                         sctpAlgorithm->sackSent();
+                         state->sackAllowed = false;
+                         sctpEV3<<"RTX: send only SACK\n";
+                         sendSACKviaSelectedPath(sctpMsg);
+                         if (datVar != NULL)  {
+                            sctpMsg = new SCTPMessage("send");
+                            sctpMsg->setByteLength(SCTP_COMMON_HEADER);
+                            headerCreated = true;
+                            sackAdded     = false;
+                            chunksAdded   = 0;
+                         }
+                      }
+                   }
                 }
-                if (datVar != NULL) {
-                    assert(datVar->getNextDestinationPath() == path);
-                    datVar->numberOfRetransmissions++;
-                    if (chunkHasBeenAcked(datVar) == false) {
-                        sctpEV3 << simTime() << ": Retransmission #" << datVar->numberOfRetransmissions
-                                  << " of TSN " << datVar->tsn
-                                  << " on path " << datVar->getNextDestination()
-                                  << " (last was " << datVar->getLastDestination() << ")" << endl;
-
-                        datVar->countsAsOutstanding = true;
-                        datVar->hasBeenReneged       = false;
-                        increaseOutstandingBytes(datVar, path); // NOTE: path == datVar->getNextDestinationPath()
-                    }
-                }
-
-                // ====== First Transmission ====================================
-                else if ( ((scount > 0) && (!state->nagleEnabled)) ||                                         // Data to send and Nagle off
-                             ((uint32)scount >= path->pmtu - 32 - 20) ||                                          // Data to fill at least one path MTU
-                             ((scount > 0) && (state->nagleEnabled) && (outstandingBytes == 0)) ) {   // Data to send, Nagle on and no outstanding bytes
 
-                    if(path == state->getPrimaryPath()) {
-
-                        // ------ Dequeue data message ----------------------------
-                        sctpEV3 << "sendAll:     sctpMsg->length=" << sctpMsg->getByteLength()
-                                  << " length datMsg=" << path->pmtu-sctpMsg->getByteLength() - 20 << endl;
-                        SCTPDataMsg* datMsg = dequeueOutboundDataMsg(path->pmtu-sctpMsg->getByteLength() - 20,
-                                                                                    allowance);
-                        if (chunksAdded==1 && sackAdded && !sackOnly && datMsg==NULL)
-                        {
-                            sctpMsg->removeChunk();
-                            delete sackChunk;
-                            datMsg = dequeueOutboundDataMsg(path->pmtu-sctpMsg->getByteLength() - 20,
-                                                                    allowance);
-                        }
-                        // ------ Handle data message -----------------------------
-                        if (datMsg) {
-                            firstTime = true;
-
-                            state->queuedMessages--;
-                            if ((state->queueLimit > 0) &&
-                                (state->queuedMessages < state->queueLimit) &&
-                                (state->queueUpdate == false)) {
-                                // Tell upper layer readiness to accept more data
-                                sendIndicationToApp(SCTP_I_SEND_MSG);
-                                state->queueUpdate = true;
-                            }
-
-                            datVar = makeDataVarFromDataMsg(datMsg, path);
-                            delete datMsg;
-
-                            sctpEV3 << "sendAll: chunk " << datVar << " dequeued from StreamQ "
-                                    << datVar->sid << ": tsn=" << datVar->tsn
-                                    << ", bytes now " << qCounter.roomSumSendStreams << "\n";
-                        }
-
-                        // ------ No data message has been dequeued ---------------
-                        else {
-                            // ------ Are there any chunks to send? ----------------
-                            if (chunksAdded == 0) {
-                                // No -> nothing more to do.
-                                if (state->sctpMsg == sctpMsg)
-                                {
-                                    state->sctpMsg = NULL;
-                                    state->packetBytes = 0;
-                                }
-                                packetFull = true;  // chunksAdded==0, packetFull==true => leave inner while loop
-                            }
-                            else {
-                                // Yes.
-                                if (state->nagleEnabled && (outstandingBytes > 0) &&
-                                    nextChunkFitsIntoPacket(path->pmtu-sctpMsg->getByteLength() - 20) &&
-                                    (sctpMsg->getByteLength() < path->pmtu - 32 - 20) && (tcount == 0))
-                                {
-                                    storePacket(path, sctpMsg, chunksAdded, dataChunksAdded, packetBytes, authAdded);
-                                    packetBytes = 0;
-                                }
-                                //chunksAdded = 0;
-                                packetFull = true;  // chunksAdded==0, packetFull==true => leave inner while loop
-                                sctpEV3 << "sendAll: packetFull: msg length = " << sctpMsg->getBitLength() / 8 + 20 << "\n";
-                            }
-                        }
-                    }
+                // Check for FORWARD-TSN again, might just have been triggered...
+                if (datVar == NULL && !forwardPresent && !state->stopSending) {
+                   if (peekAbandonedChunk(path) != NULL) {
+                      forwardChunk = createForwardTsnChunk(path->remoteAddress);
+                      chunksAdded++;
+                      totalChunksSent++;
+                      state->ackPointAdvanced = false;
+                      // ------ Create AUTH chunk, if necessary -----------------------
+                      authAdded = addAuthChunkIfNecessary(sctpMsg, FORWARD_TSN, authAdded);
+                      sctpMsg->addChunk(forwardChunk);
+                      forwardPresent = true;
+                      if (!path->T3_RtxTimer->isScheduled()) {
+                         // Start retransmission timer, if not scheduled before
+                         startTimer(path->T3_RtxTimer, path->pathRto);
+                      }
+                   }
                 }
 
-
-                // ------ Handle DATA chunk -------------------------------------
-                if (datVar != NULL && !packetFull) {
-                    // ------ Assign TSN -----------------------------------------
-                    if (firstTime) {
-                        assert(datVar->tsn == 0);
-                        datVar->tsn = state->nextTSN;
-                        sctpEV3 << "sendAll: set TSN=" << datVar->tsn
-                                << " sid=" << datVar->sid << ", ssn=" << datVar->ssn << "\n";
-                        state->nextTSN++;
-                    }
-
-                    SCTP::AssocStatMap::iterator iterator = sctpMain->assocStatMap.find(assocId);
-                    iterator->second.transmittedBytes += datVar->len / 8;
-
-                    datVar->setLastDestination(path);
-                    datVar->countsAsOutstanding = true;
-                    datVar->hasBeenReneged       = false;
-                    datVar->sendTime                 = simTime(); //I.R. to send Fast RTX just once a RTT
-
-                    // ------ First transmission of datVar -----------------------
-                    if (datVar->numberOfTransmissions == 0) {
-
-                        sctpEV3 << "sendAll: " << simTime() << " firstTime, TSN "
-                                  << datVar->tsn    << ": lastDestination set to "
-                                  << datVar->getLastDestination() << endl;
-
-                        if (!state->firstDataSent) {
-                            state->firstDataSent = true;
-                        }
-                        sctpEV3 << "sendAll: insert in retransmissionQ tsn=" << datVar->tsn << "\n";
-                        if(!retransmissionQ->checkAndInsertChunk(datVar->tsn, datVar)) {
-                            opp_error("Cannot add datVar to retransmissionQ!");
-                            // Falls es hier aufschlaegt, muss ueberlegt werden, ob es OK ist, dass datVars nicht eingefuegt werden koennen.
-                        }
-                        else {
-                            sctpEV3 << "sendAll: size of retransmissionQ=" << retransmissionQ->getQueueSize() << "\n";
-                            unackChunk(datVar);
-                            increaseOutstandingBytes(datVar, path);
-                        }
-                    }
-
-                    /* datVar is already in the retransmissionQ */
-                    datVar->numberOfTransmissions++;
-                    datVar->gapReports                  = 0;
-                    datVar->hasBeenFastRetransmitted = false;
-                    sctpEV3<<"sendAll(): adding new outbound data datVar to packet (tsn="<<datVar->tsn<<")...!!!\n";
-
-                    chunkPtr = transformDataChunk(datVar);
-
-                    /* update counters */
-                    totalChunksSent++;
-                    chunksAdded++;
-                    dataChunksAdded++;
-                    sctpMsg->addChunk(chunkPtr);
-                    if(nextChunkFitsIntoPacket(path->pmtu - sctpMsg->getByteLength() - 20) == false) {
-                        // ???? Robin: Ist diese Annahme so richtig?
-                        packetFull = true;
-                    }
-
-                    state->peerRwnd -= datVar->booksize;
-                    if ((bytes.chunk == false) && (bytes.packet == false)) {
-                        bytesToSend -= datVar->booksize;
-                    }
-                    else if (bytes.chunk) {
-                        bytes.chunk = false;
-                    }
-                    else if ((bytes.packet) && (packetFull)) {
-                        bytes.packet = false;
-                    }
-
-                    if (bytesToSend <= 0) {
-                        if ((!packetFull) && (qCounter.roomSumSendStreams > path->pmtu - 32 - 20 || tcount > 0)) {
-                            sendOneMorePacket = true;
-                            bytes.packet        = true;
-                            sctpEV3 << "sendAll: one more packet allowed\n";
-                        }
-                        else {
-                            if (state->nagleEnabled && (outstandingBytes > 0) &&
-                                nextChunkFitsIntoPacket(path->pmtu-sctpMsg->getByteLength() - 20) &&
-                                (sctpMsg->getByteLength() < path->pmtu - 32 - 20) && (tcount == 0))
-                            {
-                                storePacket(path, sctpMsg, chunksAdded, dataChunksAdded, packetBytes, authAdded);
-                                packetBytes = 0;
-                                chunksAdded = 0;
-                                packetFull  = true;  // chunksAdded==0, packetFull==true => leave inner while loop
-                            }
-                            else {
-                                packetFull            = true;
-                            }
-                        }
-                        bytesToSend = 0;
-                    }
-                    else if ((qCounter.roomSumSendStreams == 0) && (tq->second==0)) {
-                        packetFull            = true;
-                        sctpEV3 << "sendAll: no data in send and transQ: packet full\n";
-                    }
-                    sctpEV3 << "sendAll: bytesToSend after reduction: " << bytesToSend << "\n";
+                if (datVar != NULL) {
+                   assert(datVar->getNextDestinationPath() == path);
+                   datVar->numberOfRetransmissions++;
+                   if (chunkHasBeenAcked(datVar) == false) {
+                      sctpEV3 << simTime() << ": Retransmission #" << datVar->numberOfRetransmissions
+                              << " of TSN " << datVar->tsn
+                              << " on path " << datVar->getNextDestination()
+                              << " (last was " << datVar->getLastDestination() << ")" << endl;
+#ifdef SNDMESSAGE_DEBUG
+                      std::cout << simTime() << ":\t"
+                                << "Sending TSN " << datVar->tsn << " (RETRANSMISSION) on "
+                                << datVar->getNextDestinationPath()->remoteAddress
+                                << endl;
+#endif
+
+                      datVar->countsAsOutstanding = true;
+                      datVar->hasBeenReneged      = false;
+                      increaseOutstandingBytes(datVar, path);   // NOTE: path == datVar->getNextDestinationPath()
+                   }
                 }
-
-                // ------ There is no DATA chunk, only control chunks possible --
-                else {
-                    // ????? Robin: Kann dieser Fall wirklich eintreten?
-                    if (chunksAdded == 0) {   // Nothing to do -> return
-                        packetFull = true;  // chunksAdded==0, packetFull==true => leave inner while loop
-                    }
-                    else {
-                        packetFull = true;
-                        sctpEV3 << "sendAll: packetFull: msg length = " << sctpMsg->getBitLength() / 8 + 20 << "\n";
-                        datVar = NULL;
-                    }
+            }
+            // ====== First Transmission ====================================
+            else if ( ((scount > 0) && (!state->nagleEnabled)) ||                              // Data to send and Nagle off
+                      ((uint32)scount >= path->pmtu - 32 - 20) ||                              // Data to fill at least one path MTU
+                      ((scount > 0) && (state->nagleEnabled) && ((outstandingBytes == 0) || (sackOnly && sackAdded)) )) {   // Data to send, Nagle on and no outstanding bytes
+               if(path == state->getPrimaryPath()) {
+
+                  // ------ Dequeue data message ----------------------------
+                  SCTPDataMsg* datMsg = dequeueOutboundDataMsg(path, path->pmtu-sctpMsg->getByteLength() - 20,
+                                                               allowance);
+                  sctpEV3 << assocId<< ":: sendAll:  sctpMsg->length=" << sctpMsg->getByteLength()
+                          << " length datMsg=" << path->pmtu-sctpMsg->getByteLength() - 20 << "datMsg="<<&datMsg<<endl;
+
+                  if (datMsg == NULL) {
+                   if (chunksAdded==1 && sackAdded) {
+                      datMsg = dequeueOutboundDataMsg(path, path->pmtu-sctpMsg->getByteLength() + sackChunk->getByteLength() - 20,
+                                                               allowance);
+                      if (!sackOnly)  {
+                         sctpMsg->removeChunk();
+                         sctpEV3 << assocId << ": delete SACK chunk to make room for datMsg (" << &datMsg << "). scount=" << scount << "\n";
+                         delete sackChunk;
+                         chunksAdded--;
+                         sackAdded = false;
+                      }
+                      else {
+                         state->lastTransmission = simTime();
+                         state->packetsInTotalBurst++;
+                         if (dataChunksAdded > 0) {
+                            state->ssNextStream = true;
+                         }
+                         state->ackState = 0;
+                         // Stop SACK timer if it is running...
+                         stopTimer(SackTimer);
+                         sctpAlgorithm->sackSent();
+                         state->sackAllowed = false;
+                         sctpEV3 << assocId << ": send SACK and make new header for datMsg (" << &datMsg << "). scount=" << scount << "\n";
+                         sendSACKviaSelectedPath(sctpMsg);
+                         if (datMsg != NULL)  {
+                            sctpMsg = new SCTPMessage("send");
+                            sctpMsg->setByteLength(SCTP_COMMON_HEADER);
+                            headerCreated = true;
+                            sackAdded     = false;
+                            chunksAdded   = 0;
+                         }
+                      }
+                   }
                 }
 
-
-                // ====== Send packet ===========================================
-                if (packetFull) {
-                    if(chunksAdded == 0) {   // Nothing to send
-                        delete sctpMsg;
-                        sendingAllowed = false;   // sendingAllowed==false => leave outer while loop
-                    }
-                    else {
-                        sctpEV3 << "sendAll: " << simTime() << "    packet full:"
-                                << "    totalLength=" << sctpMsg->getBitLength() / 8 + 20
-                                << ",    path="   << path->remoteAddress
-                                << "     "                << dataChunksAdded << " chunks added, outstandingBytes now "
-                                << path->outstandingBytes << "\n";
-
-                        /* new chunks would exceed MTU, so we send old packet and build a new one */
-                        /* this implies that at least one data chunk is send here */
-                        if (dataChunksAdded > 0) {
-                            if (!path->T3_RtxTimer->isScheduled()) {
-                                // Start retransmission timer, if not scheduled before
-                                startTimer(path->T3_RtxTimer, path->pathRto);
-                            }
-                            else {
-                                sctpEV3 << "sendAll: RTX Timer already scheduled -> no need to schedule it\n";
-                            }
-                        }
-                        if (sendOneMorePacket) {
-                            sendOneMorePacket = false;
-                            bytesToSend         = 0;
-                            bytes.packet        = false;
-                        }
-
-
-                        sendToIP(sctpMsg, path->remoteAddress);
-                        pmDataIsSentOn(path);
-                        totalPacketsSent++;
-
-                        // ------ Reset status ------------------------------------
+                  // ------ Handle data message -----------------------------
+                  if (datMsg) {
+                     firstTime = true;
+
+                     state->queuedMessages--;
+                     if ((state->queueLimit > 0) &&
+                        (state->queuedMessages < state->queueLimit) &&
+                        (state->queueUpdate == false)) {
+                        // Tell upper layer readiness to accept more data
+                        sendIndicationToApp(SCTP_I_SEND_MSG);
+                        state->queueUpdate = true;
+                     }
+
+                     datVar = makeDataVarFromDataMsg(datMsg, path);
+                     delete datMsg;
+
+                     sctpEV3 << assocId<<":: sendAll: chunk " << datVar << " dequeued from StreamQ "
+                           << datVar->sid << ": tsn=" << datVar->tsn
+                           << ", bytes now " << qCounter.roomSumSendStreams << "\n";
+                  }
+
+                  // ------ No data message has been dequeued ---------------
+                  else {
+                     sctpEV3 << assocId << ": No data message has been dequeued" << endl;
+                     // ------ Are there any chunks to send? ----------------
+                     if (chunksAdded == 0) {
+                        // No -> nothing more to do.
                         if (state->sctpMsg == sctpMsg)
                         {
-                            state->sctpMsg = NULL;
-                            path->outstandingBytes += packetBytes;
-                            packetBytes = 0;
+                           state->sctpMsg     = NULL;
+                           state->packetBytes = 0;
                         }
-                        headerCreated    = false;
-                        chunksAdded      = 0;
-                        dataChunksAdded = 0;
-                        firstTime        = false;
-
-                        sctpEV3 << "sendAll: sending Packet to path " << path->remoteAddress
-                                  << "  scount=" << scount
-                                  << "  tcount=" << tcount
-                                  << "  bytesToSend=" << bytesToSend << endl;
-                    }
+                        packetFull = true;   // chunksAdded==0, packetFull==true => leave inner while loop
+                     }
+                     else {
+                        // Yes.
+                        if (state->nagleEnabled && ((outstandingBytes > 0) && !(sackOnly && sackAdded)) &&
+                           nextChunkFitsIntoPacket(path, path->pmtu-sctpMsg->getByteLength() - 20) &&
+                           (sctpMsg->getByteLength() < path->pmtu - 32 - 20) && (tcount == 0))
+                        {
+                           sctpEV3<<"Nagle: Packet has to be stored\n";
+                           storePacket(path, sctpMsg, chunksAdded, dataChunksAdded, authAdded);
+                           sctpMsg     = NULL;
+                           chunksAdded = 0;
+                        }
+                        //chunksAdded = 0;
+                        packetFull = true;   // chunksAdded==0, packetFull==true => leave inner while loop
+                        sctpEV3 << "sendAll: packetFull: msg length = " << sctpMsg->getByteLength() + 20 << "\n";
+                     }
+                  }
+               }
+               else if (chunksAdded==1 && sackAdded && !sackOnly)  {
+                   sctpMsg->removeChunk();
+                   sctpEV3 << "Nagle or no data: Remove SACK chunk, delete sctpmsg" << endl;
+                   delete sackChunk;
+                   packetFull = true;
+                   sackAdded = false;
+                   chunksAdded--;
                 }
-                sctpEV3 << "sendAll: still " << bytesToSend
-                          << " bytes to send, headerCreated=" << headerCreated << endl;
-
-            }    // if (bytesToSend > 0 || bytes.chunk || bytes.packet)
+                else if ((chunksAdded==1 && sackAdded && sackOnly) || headerCreated )  {
+                   packetFull = true;
+                }
+            }
+            else if (chunksAdded==1 && sackAdded && !sackOnly)  {
+               sctpMsg->removeChunk();
+               sctpEV3<<"Nagle or no data: Remove SACK chunk, delete sctpmsg\n";
+               delete sackChunk;
+               packetFull = true;
+               sackAdded = false;
+               chunksAdded--;
+            }
+            else if (chunksAdded==1 && sackAdded && sackOnly)  {
+               packetFull = true;
+            }
+            else if (datVar==NULL || chunksAdded == 0)
+            {
+                sctpEV3<<"HeaderCreated="<<headerCreated<<", chunksAdded="<<chunksAdded<<" datVar="<<datVar<<"\n";
+                if (headerCreated)
+                {
+                    packetFull = true;
+                }
+            }
+            // ------ Handle DATA chunk -------------------------------------
+            if (datVar != NULL && !packetFull) {
+               // ------ Assign TSN -----------------------------------------
+               if (firstTime) {
+                  assert(datVar->tsn == 0);
+                  datVar->tsn = state->nextTSN;
+                  chunkMap->setChunk(datVar->tsn, datVar);
+                  sctpEV3 << "sendAll: set TSN=" << datVar->tsn
+                        << " sid=" << datVar->sid << ", ssn=" << datVar->ssn << "\n";
+                  state->nextTSN++;
+                  path->vectorPathSentTSN->record(datVar->tsn);
+               }
+               else {
+                  if(datVar->hasBeenFastRetransmitted)  {
+                     path->vectorPathTSNFastRTX->record(datVar->tsn);
+                  }
+                  else {
+                     path->vectorPathTSNTimerBased->record(datVar->tsn);
+                  }
+               }
+
+               SCTP::AssocStatMap::iterator iterator = sctpMain->assocStatMap.find(assocId);
+               iterator->second.transmittedBytes += datVar->len / 8;
+               sctpEV3<<assocId<<": added "<<datVar->len / 8<<" bytes; transmittedBytes now="<<iterator->second.transmittedBytes<<" ackedBytes="<<iterator->second.ackedBytes<<"\n";
+               datVar->setLastDestination(path);
+               datVar->countsAsOutstanding = true;
+               datVar->hasBeenReneged      = false;
+               datVar->sendTime            = simTime(); //I.R. to send Fast RTX just once a RTT
+               if(datVar->firstSendTime == 0) {
+                  datVar->firstSendTime = simTime();
+               }
+
+               // ------ First transmission of datVar -----------------------
+               if (datVar->numberOfTransmissions == 0) {
+                  sctpEV3 << "sendAll: " << simTime() << " firstTime, TSN "
+                          << datVar->tsn  << ": lastDestination set to "
+                          << datVar->getLastDestination() << endl;
+
+                  if (!state->firstDataSent) {
+                     state->firstDataSent = true;
+                  }
+                  sctpEV3 << "sendAll: insert in retransmissionQ tsn=" << datVar->tsn << "\n";
+                  if(!retransmissionQ->checkAndInsertChunk(datVar->tsn, datVar)) {
+                     opp_error("Cannot add datVar to retransmissionQ!");
+                     // Falls es hier aufschlaegt, muss ueberlegt werden, ob es OK ist, dass datVars nicht eingefuegt werden koennen.
+                  }
+                  else {
+                     sctpEV3 << "sendAll: size of retransmissionQ=" << retransmissionQ->getQueueSize() << "\n";
+                     unackChunk(datVar);
+                     increaseOutstandingBytes(datVar, path);
+                     datVar->queuedOnPath = path;
+                     datVar->queuedOnPath->queuedBytes += datVar->booksize;
+                     datVar->queuedOnPath->statisticsPathQueuedSentBytes->record(path->queuedBytes);
+
+                     state->queuedSentBytes += datVar->booksize;
+                     statisticsQueuedSentBytes->record(state->queuedSentBytes);
+                  }
+               }
+
+               /* datVar is already in the retransmissionQ */
+               datVar->numberOfTransmissions++;
+               datVar->gapReports               = 0;
+               datVar->hasBeenFastRetransmitted = false;
+               sctpEV3<<"sendAll(): adding new outbound data datVar to packet (tsn="<<datVar->tsn<<")...!!!\n";
+
+               chunkPtr = transformDataChunk(datVar);
+
+               /* update counters */
+               totalChunksSent++;
+               chunksAdded++;
+               dataChunksAdded++;
+               sctpEV3<<assocId<<": DataChunk with TSN="<<chunkPtr->getTsn()<<" and length "<<chunkPtr->getByteLength()<<" added\n";
+               sctpMsg->addChunk(chunkPtr);
+               if (datVar->numberOfTransmissions>1)  {
+                     CounterMap::iterator tq = qCounter.roomTransQ.find(path->remoteAddress);
+                     if (tq->second > 0)  {
+                        if (transmissionQ->getSizeOfFirstChunk(path->remoteAddress) > path->pmtu - sctpMsg->getByteLength() - 20)
+                           packetFull = true;
+                     }
+                     else if (nextChunkFitsIntoPacket(path, path->pmtu - sctpMsg->getByteLength() - 20) == false) {
+                        packetFull = true;
+                  }
+               }
+               else  {
+                  if (nextChunkFitsIntoPacket(path, path->pmtu - sctpMsg->getByteLength() - 20) == false) {
+                     packetFull = true;
+                  }
+               }
+
+               state->peerRwnd -= (datVar->booksize + state->bytesToAddPerPeerChunk);
+               if (state->peerAllowsChunks) {
+                  state->peerMsgRwnd--;
+               }
+               if ((bytes.chunk == false) && (bytes.packet == false)) {
+                  bytesToSend -= datVar->booksize;
+               }
+               else if (bytes.chunk) {
+                  bytes.chunk = false;
+               }
+               else if ((bytes.packet) && (packetFull)) {
+                  bytes.packet = false;
+               }
+
+               if (bytesToSend <= 0) {
+                  if ((!packetFull) && (qCounter.roomSumSendStreams > path->pmtu - 32 - 20 || tcount > 0)) {
+                     sendOneMorePacket = true;
+                     bytes.packet      = true;
+                     sctpEV3 << assocId<<": sendAll: one more packet allowed\n";
+                  }
+                  else {
+                     if (state->nagleEnabled && (outstandingBytes > 0) &&
+                        nextChunkFitsIntoPacket(path, path->pmtu-sctpMsg->getByteLength() - 20) &&
+                        (sctpMsg->getByteLength() < path->pmtu - 32 - 20) && (tcount == 0))
+                     {
+                        storePacket(path, sctpMsg, chunksAdded, dataChunksAdded, authAdded);
+                        sctpMsg     = NULL;
+                        chunksAdded = 0;
+                        packetFull  = true;   // chunksAdded==0, packetFull==true => leave inner while loop
+                     }
+                     else {
+                        packetFull = true;
+                     }
+                  }
+                  bytesToSend = 0;
+               }
+               else if ((qCounter.roomSumSendStreams == 0) && (tq->second == 0)) {
+                  packetFull = true;
+                  sctpEV3 << "sendAll: no data in send and transQ: packet full\n";
+               }
+               sctpEV3 << "sendAll: bytesToSend after reduction: " << bytesToSend << "\n";
+            }  // end if (datVar != NULL && !packetFull)
+
+            // ------ There is no DATA chunk, only control chunks possible --
             else {
-                packetFull = true;  // Leave inner while loop
-                delete sctpMsg;     // T.D. 19.01.2010: Free unsent message
+               if (chunksAdded == 0) {   // Nothing to do -> return
+                  packetFull = true;     // chunksAdded==0, packetFull==true => leave inner while loop
+               }
+               else {
+                  packetFull = true;
+                  sctpEV3 << assocId <<": sendAll: packetFull: msg length = "
+                          << sctpMsg->getBitLength() / 8 + 20 << endl;
+                  datVar = NULL;
+               }
             }
 
-            sctpEV3 << "packetFull=" << packetFull << endl;
-        }    // while(!packetFull)
 
-        sctpEV3 << "bytesToSend="    << bytesToSend
-                  << " bytes.chunk="     << bytes.chunk
-                  << " bytes.packet=" << bytes.packet << endl;
-        if (!(bytesToSend > 0 || bytes.chunk || bytes.packet)) {
-            sendingAllowed = false;
-        }
-    }    // while(sendingAllowed)
+            // ====== Send packet ===========================================
+            if (packetFull) {
+               if(chunksAdded == 0) {   // Nothing to send
+                  delete sctpMsg;
+                  sendingAllowed = false;   // sendingAllowed==false => leave outer while loop
+               }
+               else {
+                  sctpEV3 << assocId <<":: sendAll: " << simTime() << "  packet full:"
+                        << " totalLength=" << sctpMsg->getBitLength() / 8 + 20
+                        << ", path="       << path->remoteAddress
+                        << ", "            << dataChunksAdded
+                        << " data chunks added, outstandingBytes now "
+                        << path->outstandingBytes << endl;
+
+                  /* new chunks would exceed MTU, so we send old packet and build a new one */
+                  /* this implies that at least one data chunk is send here */
+                  if (dataChunksAdded > 0) {
+                     if (!path->T3_RtxTimer->isScheduled()) {
+                        // Start retransmission timer, if not scheduled before
+                        startTimer(path->T3_RtxTimer, path->pathRto);
+                     }
+                     else {
+                        sctpEV3 << "sendAll: RTX Timer already scheduled -> no need to schedule it\n";
+                     }
+                  }
+                  if (sendOneMorePacket) {
+                     sendOneMorePacket = false;
+                     bytesToSend       = 0;
+                     bytes.packet      = false;
+                     chunkPtr->setIBit(sctpMain->sackNow);
+                  }
+
+                  if (dataChunksAdded > 0) {
+                     state->ssNextStream = true;
+                  }
+                  sctpEV3<<assocId<<":sendToIP: packet size="<<sctpMsg->getByteLength()<<" numChunks="<<sctpMsg->getChunksArraySize()<<"\n";
+                  sendToIP(sctpMsg, path->remoteAddress);
+                  recordTransmission(sctpMsg, path);
+                  pmDataIsSentOn(path);
+                  totalPacketsSent++;
+                  state->lastTransmission = simTime();
+                  state->packetsInTotalBurst++;
+
+#ifdef SNDMESSAGE_DEBUG
+                  const SCTPDataChunk* dataChunkPtr = dynamic_cast<const SCTPDataChunk*>(chunkPtr);
+                  if(dataChunkPtr != NULL) {
+                     std::cout << simTime() << ":\t"
+                               << "Sending TSN " << dataChunkPtr->getTsn() << " on "
+                               << path->remoteAddress
+                               << endl;
+                  }
+#endif
+
+                  // ------ Reset status ------------------------------------
+                  firstTime       = false;
+                  headerCreated   = false;
+                  chunksAdded     = 0;
+                  dataChunksAdded = 0;
+                  authAdded       = false;
+
+                  sctpEV3 << "sendAll: sending Packet to path " << path->remoteAddress
+                          << "  scount=" << scount
+                          << "  tcount=" << tcount
+                          << "  bytesToSend=" << bytesToSend << endl;
+               }
+            }
+            sctpEV3 << "sendAll: still " << bytesToSend
+                    << " bytes to send, headerCreated=" << headerCreated << endl;
+
+         }   // if (bytesToSend > 0 || bytes.chunk || bytes.packet)
+         else {
+            packetFull = true;   // Leave inner while loop
+            delete sctpMsg;      // T.D. 19.01.2010: Free unsent message
+         }
+
+         sctpEV3 << "packetFull=" << packetFull << endl;
+      }   // while(!packetFull)
+
+      sctpEV3 << "bytesToSend="   << bytesToSend
+              << " bytes.chunk="  << bytes.chunk
+              << " bytes.packet=" << bytes.packet << endl;
+      if (!(bytesToSend > 0 || bytes.chunk || bytes.packet)) {
+         sendingAllowed = false;
+      }
+   }   // while(sendingAllowed)
+
+   sctpEV3 << "sendAll: nothing more to send... BYE!\n";
+   if(SCTP::checkQueues) {
+      checkOutstandingBytes();
+   }
+}
 
-    sctpEV3 << "sendAll: nothing more to send... BYE!\n";
+
+uint32 SCTPAssociation::getAllTransQ()
+{
+   uint32 sum = 0;
+   for (CounterMap::iterator tq = qCounter.roomTransQ.begin(); tq!=qCounter.roomTransQ.end(); tq++) {
+      sum += tq->second;
+   }
+   return sum;
 }
diff --git a/src/transport/sctp/SCTPAssociationStreamReset.cc b/src/transport/sctp/SCTPAssociationStreamReset.cc
new file mode 100644
index 0000000..0b2ed8b
--- /dev/null
+++ b/src/transport/sctp/SCTPAssociationStreamReset.cc
@@ -0,0 +1,284 @@
+//
+// Copyright (C) 2008 Irene Ruengeler
+// Copyright (C) 2009-2012 Thomas Dreibholz
+//
+// 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
+// of the License, 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 this program; if not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "SCTPAssociation.h"
+#include "SCTPCommand_m.h"
+
+void SCTPAssociation::retransmitReset()
+{
+   SCTPMessage *sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   SCTPStreamResetChunk *sctpreset = new SCTPStreamResetChunk("RESET");
+   sctpreset=check_and_cast<SCTPStreamResetChunk *>(state->resetChunk->dup());
+   sctpreset->setChunkType(STREAM_RESET);
+   sctpmsg->addChunk(sctpreset);
+
+   sctpEV3<<"retransmitStreamReset localAddr="<<localAddr<<"  remoteAddr"<<remoteAddr<<"\n";
+
+   sendToIP(sctpmsg);
+}
+
+void SCTPAssociation::sendOutgoingResetRequest(SCTPIncomingSSNResetRequestParameter*requestParam)
+{
+   sctpEV3<<"sendOutgoingResetRequest to "<<remoteAddr<<"\n";
+   SCTPStreamResetChunk* resetChunk = new SCTPStreamResetChunk("STREAM_RESET");
+   resetChunk->setChunkType(STREAM_RESET);
+   resetChunk->setBitLength((SCTP_STREAM_RESET_CHUNK_LENGTH)*8);
+   uint32 srsn = state->streamResetSequenceNumber;
+   SCTPOutgoingSSNResetRequestParameter* outResetParam;
+   outResetParam = new SCTPOutgoingSSNResetRequestParameter("Outgoing_Request_Param");
+   outResetParam->setParameterType(OUTGOING_RESET_REQUEST_PARAMETER);
+   outResetParam->setSrReqSn(srsn++);
+   outResetParam->setSrResSn(requestParam->getSrReqSn());
+   outResetParam->setLastTsn(state->nextTSN-1);
+   outResetParam->setBitLength(SCTP_OUTGOING_RESET_REQUEST_PARAMETER_LENGTH*8);
+   resetChunk->addParameter(outResetParam);
+   state->streamResetSequenceNumber = srsn;
+
+
+   if (!(getPath(remoteAddr)->ResetTimer->isScheduled()))
+   {
+      SCTPResetTimer* rt = new SCTPResetTimer();
+      rt->setInSN(0);
+      rt->setInAcked(true);
+      rt->setOutSN(srsn-1);
+      rt->setOutAcked(false);
+      PK(getPath(remoteAddr)->ResetTimer)->encapsulate(rt);
+      sctpEV3<<"encapsulate new timer data for "<<getPath(remoteAddr)->ResetTimer->getName()<<", start timer with rto="<<getPath(remoteAddr)->pathRto<<"\n";
+      SCTPResetTimer* tm = check_and_cast<SCTPResetTimer*>(PK(getPath(remoteAddr)->ResetTimer)->decapsulate());
+      sctpEV3<<"outSN="<<tm->getOutSN()<<"\n";
+      PK(getPath(remoteAddr)->ResetTimer)->encapsulate(tm);
+      startTimer(getPath(remoteAddr)->ResetTimer, getPath(remoteAddr)->pathRto);
+      sctpEV3<<"make new message and send\n";
+      SCTPMessage *msg = new SCTPMessage();
+      msg->setBitLength(SCTP_COMMON_HEADER*8);
+      msg->setSrcPort(localPort);
+      msg->setDestPort(remotePort);
+      msg->addChunk(resetChunk);
+      state->resetChunk=check_and_cast<SCTPStreamResetChunk*>(resetChunk->dup());
+
+      sendToIP(msg, remoteAddr);
+   }
+}
+
+
+SCTPParameter* SCTPAssociation::makeOutgoingStreamResetParameter(uint32 srsn)
+{
+   SCTPOutgoingSSNResetRequestParameter* outResetParam;
+   outResetParam = new SCTPOutgoingSSNResetRequestParameter("Outgoing_Request_Param");
+   outResetParam->setParameterType(OUTGOING_RESET_REQUEST_PARAMETER);
+   outResetParam->setSrReqSn(srsn);
+   outResetParam->setSrResSn(state->expectedStreamResetSequenceNumber - 3);
+   outResetParam->setLastTsn(state->nextTSN-1);
+   outResetParam->setBitLength(SCTP_OUTGOING_RESET_REQUEST_PARAMETER_LENGTH*8);
+   return outResetParam;
+}
+
+SCTPParameter* SCTPAssociation::makeIncomingStreamResetParameter(uint32 srsn)
+{
+   SCTPIncomingSSNResetRequestParameter* inResetParam;
+   inResetParam = new SCTPIncomingSSNResetRequestParameter("Incoming_Request_Param");
+   inResetParam->setParameterType(INCOMING_RESET_REQUEST_PARAMETER);
+   inResetParam->setSrReqSn(srsn);
+   inResetParam->setBitLength(SCTP_INCOMING_RESET_REQUEST_PARAMETER_LENGTH*8);
+   return inResetParam;
+}
+
+SCTPParameter* SCTPAssociation::makeSSNTSNResetParameter(uint32 srsn)
+{
+   SCTPSSNTSNResetRequestParameter* resetParam;
+   resetParam = new SCTPSSNTSNResetRequestParameter("SSN_TSN_Request_Param");
+   resetParam->setParameterType(SSN_TSN_RESET_REQUEST_PARAMETER);
+   resetParam->setSrReqSn(srsn);
+   resetParam->setBitLength(SCTP_SSN_TSN_RESET_REQUEST_PARAMETER_LENGTH*8);
+   return resetParam;
+}
+
+void SCTPAssociation::sendOutgoingRequestAndResponse(uint32 inRequestSn, uint32 outRequestSn)
+{
+   sctpEV3<<"sendOutgoingResetRequest to "<<remoteAddr<<"\n";
+   SCTPMessage *msg = new SCTPMessage();
+   msg->setBitLength(SCTP_COMMON_HEADER*8);
+   msg->setSrcPort(localPort);
+   msg->setDestPort(remotePort);
+   SCTPStreamResetChunk* resetChunk = new SCTPStreamResetChunk("STREAM_RESET");
+   resetChunk->setChunkType(STREAM_RESET);
+   resetChunk->setBitLength((SCTP_STREAM_RESET_CHUNK_LENGTH)*8);
+   uint32 srsn = state->streamResetSequenceNumber;
+   SCTPResetTimer* rt = new SCTPResetTimer();
+   SCTPOutgoingSSNResetRequestParameter* outResetParam;
+   outResetParam = new SCTPOutgoingSSNResetRequestParameter("Outgoing_Request_Param");
+   outResetParam->setParameterType(OUTGOING_RESET_REQUEST_PARAMETER);
+   outResetParam->setSrReqSn(srsn++);
+   outResetParam->setSrResSn(inRequestSn);
+   outResetParam->setLastTsn(state->nextTSN-1);
+   outResetParam->setBitLength(SCTP_OUTGOING_RESET_REQUEST_PARAMETER_LENGTH*8);
+   resetChunk->addParameter(outResetParam);
+   state->streamResetSequenceNumber = srsn;
+   SCTPStreamResetResponseParameter* responseParam = new SCTPStreamResetResponseParameter("Response_Param");
+   responseParam->setParameterType(STREAM_RESET_RESPONSE_PARAMETER);
+   responseParam->setSrResSn(outRequestSn);
+   responseParam->setResult(1);
+   responseParam->setBitLength(SCTP_STREAM_RESET_RESPONSE_PARAMETER_LENGTH*8);
+   resetChunk->addParameter(responseParam);
+   rt->setInSN(srsn-1);
+   rt->setInAcked(false);
+   rt->setOutSN(outRequestSn);
+   rt->setOutAcked(false);
+   state->resetChunk=check_and_cast<SCTPStreamResetChunk*>(resetChunk->dup());
+   msg->addChunk(resetChunk);
+   sendToIP(msg, remoteAddr);
+   PK(getPath(remoteAddr)->ResetTimer)->encapsulate(rt);
+   startTimer(getPath(remoteAddr)->ResetTimer, getPath(remoteAddr)->pathRto);
+}
+
+void SCTPAssociation::sendStreamResetRequest(uint16 type)
+{
+   sctpEV3<<"Util:sendStreamResetRequest\n";
+   SCTPParameter* param;
+   SCTPMessage *msg = new SCTPMessage();
+   msg->setBitLength(SCTP_COMMON_HEADER*8);
+   msg->setSrcPort(localPort);
+   msg->setDestPort(remotePort);
+   SCTPStreamResetChunk* resetChunk = new SCTPStreamResetChunk("STREAM_RESET");
+   resetChunk->setChunkType(STREAM_RESET);
+   resetChunk->setBitLength((SCTP_STREAM_RESET_CHUNK_LENGTH)*8);
+   uint32 srsn = state->streamResetSequenceNumber;
+   SCTPResetTimer* rt = new SCTPResetTimer();
+   switch (type)
+   {
+      case RESET_OUTGOING:
+         sctpEV3<<"RESET_OUTGOING\n";
+         param = makeOutgoingStreamResetParameter(srsn);
+         resetChunk->addParameter(param);
+         rt->setInSN(0);
+         rt->setInAcked(true);
+         rt->setOutSN(srsn);
+         rt->setOutAcked(false);
+         break;
+      case RESET_INCOMING:
+         param = makeIncomingStreamResetParameter(srsn);
+         resetChunk->addParameter(param);
+         rt->setInSN(srsn);
+         rt->setInAcked(false);
+         rt->setOutSN(0);
+         rt->setOutAcked(true);
+         break;
+      case RESET_BOTH:
+         SCTPParameter* inParam;
+         inParam = makeIncomingStreamResetParameter(srsn);
+         rt->setInSN(srsn++);
+         rt->setInAcked(false);
+         resetChunk->addParameter(inParam);
+         SCTPParameter* outParam;
+         outParam = makeOutgoingStreamResetParameter(srsn);
+         resetChunk->addParameter(outParam);
+         rt->setOutSN(srsn);
+         rt->setOutAcked(false);
+         break;
+      case SSN_TSN:
+         param = makeSSNTSNResetParameter(srsn);
+         resetChunk->addParameter(param);
+         state->stopReceiving = true;
+         rt->setInSN(0);
+         rt->setInAcked(true);
+         rt->setOutSN(srsn);
+         rt->setOutAcked(false);
+         break;
+   }
+   state->streamResetSequenceNumber = ++srsn;
+   state->resetChunk=check_and_cast<SCTPStreamResetChunk*>(resetChunk->dup());
+   msg->addChunk(resetChunk);
+   sendToIP(msg, remoteAddr);
+
+   PK(getPath(remoteAddr)->ResetTimer)->encapsulate(rt);
+   startTimer(getPath(remoteAddr)->ResetTimer, getPath(remoteAddr)->pathRto);
+}
+
+void SCTPAssociation::sendStreamResetResponse(SCTPSSNTSNResetRequestParameter* requestParam, bool options)
+{
+   uint32 len = 0;
+   sctpEV3<<"sendStreamResetResponse to "<<remoteAddr<<" with options\n";
+   SCTPMessage *msg = new SCTPMessage();
+   msg->setBitLength(SCTP_COMMON_HEADER*8);
+   msg->setSrcPort(localPort);
+   msg->setDestPort(remotePort);
+   SCTPStreamResetChunk* resetChunk = new SCTPStreamResetChunk("STREAM_RESET");
+   resetChunk->setChunkType(STREAM_RESET);
+   resetChunk->setBitLength((SCTP_STREAM_RESET_CHUNK_LENGTH)*8);
+   SCTPStreamResetResponseParameter* responseParam = new SCTPStreamResetResponseParameter("Response_Param");
+   responseParam->setParameterType(STREAM_RESET_RESPONSE_PARAMETER);
+   responseParam->setSrResSn(requestParam->getSrReqSn());
+   responseParam->setResult(1);
+   len = SCTP_STREAM_RESET_RESPONSE_PARAMETER_LENGTH*8;
+   if (options)
+   {
+      responseParam->setSendersNextTsn(state->nextTSN);
+      responseParam->setReceiversNextTsn(state->gapList.getHighestTSNReceived() + state->localRwnd);
+      state->gapList.forwardCumAckTSN(state->gapList.getHighestTSNReceived() + state->localRwnd - 1);
+      state->peerTsnAfterReset = state->gapList.getHighestTSNReceived() + state->localRwnd;
+      resetSsns();
+      resetExpectedSsns();
+      state->stopOldData = true;
+      len += 64;
+   }
+   responseParam->setBitLength(len);
+   resetChunk->addParameter(responseParam);
+   msg->addChunk(resetChunk);
+   sendToIP(msg);
+}
+
+void SCTPAssociation::sendStreamResetResponse(uint32 srrsn)
+{
+   uint32 len = 0;
+   SCTPStreamResetChunk* resetChunk;
+   sctpEV3<<"sendStreamResetResponse to "<<remoteAddr<<"\n";
+   SCTPMessage *msg = new SCTPMessage();
+   msg->setBitLength(SCTP_COMMON_HEADER*8);
+   msg->setSrcPort(localPort);
+   msg->setDestPort(remotePort);
+   resetChunk = new SCTPStreamResetChunk("STREAM_RESET");
+   resetChunk->setChunkType(STREAM_RESET);
+   resetChunk->setBitLength((SCTP_STREAM_RESET_CHUNK_LENGTH)*8);
+   SCTPStreamResetResponseParameter* responseParam = new SCTPStreamResetResponseParameter("Response_Param");
+   responseParam->setParameterType(STREAM_RESET_RESPONSE_PARAMETER);
+   responseParam->setSrResSn(srrsn);
+   responseParam->setResult(1);
+   len = SCTP_STREAM_RESET_RESPONSE_PARAMETER_LENGTH*8;
+   responseParam->setBitLength(len);
+   resetChunk->addParameter(responseParam);
+   msg->addChunk(resetChunk);
+   sendToIP(msg);
+}
+
+
+void SCTPAssociation::resetExpectedSsns()
+{
+   for (SCTPReceiveStreamMap::iterator iter=receiveStreams.begin(); iter!=receiveStreams.end(); iter++)
+      iter->second->setExpectedStreamSeqNum(0);
+   sctpEV3<<"Expected Ssns have been resetted on "<<localAddr<<"\n";
+   sendIndicationToApp(SCTP_I_RCV_STREAMS_RESETTED);
+}
+
+void SCTPAssociation::resetSsns()
+{
+   for (SCTPSendStreamMap::iterator iter=sendStreams.begin(); iter!=sendStreams.end(); iter++)
+      iter->second->setNextStreamSeqNum(0);
+   sctpEV3<<"SSns resetted on "<<localAddr<<"\n";
+   sendIndicationToApp(SCTP_I_SEND_STREAMS_RESETTED);
+}
diff --git a/src/transport/sctp/SCTPAssociationUtil.cc b/src/transport/sctp/SCTPAssociationUtil.cc
index e893cf1..07181c7 100644
--- a/src/transport/sctp/SCTPAssociationUtil.cc
+++ b/src/transport/sctp/SCTPAssociationUtil.cc
@@ -1,6 +1,6 @@
 //
-// Copyright (C) 2005-2010 Irene Ruengeler
-// Copyright (C) 2009-2010 Thomas Dreibholz
+// Copyright (C) 2008 Irene Ruengeler
+// Copyright (C) 2009-2012 Thomas Dreibholz
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -9,7 +9,7 @@
 //
 // 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
+// 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
@@ -17,9 +17,6 @@
 //
 
 
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
 #include "SCTP.h"
 #include "SCTPAssociation.h"
 #include "SCTPCommand_m.h"
@@ -33,2067 +30,2577 @@
 #include "IPv4InterfaceData.h"
 #include "IPv6InterfaceData.h"
 #include "IPv6Address.h"
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/time.h>
 #include "UDPControlInfo_m.h"
 
 
+void SCTPAssociation::calculateRcvBuffer()
+{
+   if(SCTP::testing == true) {
+      uint32 sumDelivery  = 0;
+      uint32 sumOrdered   = 0;
+      uint32 sumUnOrdered = 0;
+      for (SCTPReceiveStreamMap::const_iterator iterator = receiveStreams.begin();
+           iterator != receiveStreams.end(); iterator++) {
+         const SCTPReceiveStream* stream = iterator->second;
+         sumDelivery  += stream->getDeliveryQ()->getQueueSize();
+         sumOrdered   += stream->getOrderedQ()->getQueueSize();
+         sumUnOrdered += stream->getUnorderedQ()->getQueueSize();
+      }
+      sctpEV3 << "DeliveryQ= "  << sumDelivery
+              << ", OrderedQ="  << sumOrdered
+              << ", UnorderedQ="<< sumUnOrdered
+              << ", bufferedMessages=" << state->bufferedMessages
+              << endl;
+   }
+}
+
+void SCTPAssociation::listOrderedQ()
+{
+   for (SCTPReceiveStreamMap::iterator iter=receiveStreams.begin(); iter!=receiveStreams.end(); iter++)
+   {
+      sctpEV3<<"stream "<<iter->second->getStreamId()<<":\n";
+      iter->second->getOrderedQ()->printQueue();
+      sctpEV3<<"\n";
+   }
+}
+
+
+//
+// helper functions
+//
+
+void SCTPAssociation::checkOutstandingBytes()
+{
+   bool                                       problem                       = false;
+   unsigned int                               totalOutstandingBytesExpected = 0;
+   unsigned int                               totalOutstandingBytesCounted  = 0;
+   std::map<const SCTPPathVariables*, uint32> outstandingBytesOnPath;
+   std::map<const SCTPPathVariables*, uint32> queuedBytesOnPath;
+
+   for (SCTPPathMap::iterator iterator = sctpPathMap.begin(); iterator != sctpPathMap.end(); ++iterator) {
+      SCTPPathVariables* path = iterator->second;
+      outstandingBytesOnPath[path] = 0;
+      queuedBytesOnPath[path]      = 0;
+      totalOutstandingBytesExpected += path->outstandingBytes;
+   }
+
+   for (SCTPQueue::PayloadQueue::const_iterator iterator = retransmissionQ->payloadQueue.begin();
+        iterator != retransmissionQ->payloadQueue.end(); ++iterator) {
+      const SCTPDataVariables* chunk = iterator->second;
+      SCTPPathVariables* lastPath    = chunk->getLastDestinationPath();
+      if(chunk->countsAsOutstanding) {
+         outstandingBytesOnPath[lastPath] += chunk->booksize;
+         totalOutstandingBytesCounted += chunk->booksize;
+      }
+      if(chunk->enqueuedInTransmissionQ) {
+         assert(transmissionQ->getChunk(chunk->tsn) != NULL);
+      }
+      queuedBytesOnPath[chunk->queuedOnPath] += chunk->booksize;
+   }
+
+   for (SCTPPathMap::iterator iterator = sctpPathMap.begin();
+        iterator != sctpPathMap.end(); ++iterator) {
+      const SCTPPathVariables* path = iterator->second;
+      sctpEV3 << "- " << outstandingBytesOnPath[path]
+              << " (outstanding is " << path->outstandingBytes << ")"
+              << ", booked "         << queuedBytesOnPath[path]
+              << " (queuedOnPath="   << path->queuedBytes << ","
+              << " roomTransQ="      << qCounter.roomTransQ.find(path->remoteAddress)->second   << ","
+              << " bookedTransQ="    << qCounter.bookedTransQ.find(path->remoteAddress)->second << ","
+              << " roomRetransQ="    << qCounter.roomRetransQ.find(path->remoteAddress)->second << ")"
+              << " on path "         << path->remoteAddress << endl;
+
+      problem = (problem || (path->outstandingBytes != outstandingBytesOnPath[path]));
+      problem = (problem || ((int32)qCounter.roomRetransQ.find(path->remoteAddress)->second < 0));
+      problem = (problem || (path->queuedBytes != queuedBytesOnPath[path]));
+   }
+   sctpEV3 << "=> " << totalOutstandingBytesCounted << " total" << endl;
+   if(problem) {
+      std::cerr << "BOOKKEEPING PROBLEM DETECTED!"
+                << "\tcountedOSB="  << totalOutstandingBytesCounted
+                << ", expectedOSB=" << totalOutstandingBytesExpected << endl;
+      for (SCTPPathMap::iterator iterator = sctpPathMap.begin();
+         iterator != sctpPathMap.end(); ++iterator) {
+         const SCTPPathVariables* path = iterator->second;
+         std::cerr << "- " << outstandingBytesOnPath[path]
+                   << " (outstanding is " << path->outstandingBytes << ")"
+                   << ", booked "         << queuedBytesOnPath[path]
+                   << " (queuedOnPath="   << path->queuedBytes << ","
+                   << " roomTransQ="      << qCounter.roomTransQ.find(path->remoteAddress)->second   << ","
+                   << " bookedTransQ="    << qCounter.bookedTransQ.find(path->remoteAddress)->second << ","
+                   << " roomRetransQ="    << qCounter.roomRetransQ.find(path->remoteAddress)->second << ")"
+                   << " on path "         << path->remoteAddress << endl;
+         if(path->outstandingBytes != outstandingBytesOnPath[path]) {
+            std::cerr << "ERROR: Path outstanding bytes mismatch!" << std::endl;
+         }
+         if(path->queuedBytes != queuedBytesOnPath[path]) {
+            std::cerr << "ERROR: Path queued bytes mismatch!" << std::endl;
+         }
+      }
+      assert(false);
+   }
+   assert(state->outstandingBytes == totalOutstandingBytesCounted);
+}
+
+
 
 void SCTPAssociation::printSctpPathMap() const
 {
-    sctpEV3 <<"SCTP PathMap:" << endl;
-    for (SCTPPathMap::const_iterator iterator = sctpPathMap.begin();
-          iterator != sctpPathMap.end(); ++iterator) {
-        const SCTPPathVariables* path = iterator->second;
-        sctpEV3 << " - " << path->remoteAddress << ":  osb=" << path->outstandingBytes
-                  << " cwnd=" << path->cwnd << endl;
-    }
+   sctpEV3 <<"SCTP PathMap:" << endl;
+   for (SCTPPathMap::const_iterator iterator = sctpPathMap.begin();
+        iterator != sctpPathMap.end(); ++iterator) {
+      const SCTPPathVariables* path = iterator->second;
+      sctpEV3 << " - " << path->remoteAddress << ":  osb=" << path->outstandingBytes
+              << " cwnd=" << path->cwnd << endl;
+   }
 }
 
 const char* SCTPAssociation::stateName(const int32 state)
 {
 #define CASE(x) case x: s=#x+7; break
-    const char* s = "unknown";
-    switch (state) {
-        CASE(SCTP_S_CLOSED);
-        CASE(SCTP_S_COOKIE_WAIT);
-        CASE(SCTP_S_COOKIE_ECHOED);
-        CASE(SCTP_S_ESTABLISHED);
-        CASE(SCTP_S_SHUTDOWN_PENDING);
-        CASE(SCTP_S_SHUTDOWN_SENT);
-        CASE(SCTP_S_SHUTDOWN_RECEIVED);
-        CASE(SCTP_S_SHUTDOWN_ACK_SENT);
-    }
-    return s;
+   const char* s = "unknown";
+   switch (state) {
+      CASE(SCTP_S_CLOSED);
+      CASE(SCTP_S_COOKIE_WAIT);
+      CASE(SCTP_S_COOKIE_ECHOED);
+      CASE(SCTP_S_ESTABLISHED);
+      CASE(SCTP_S_SHUTDOWN_PENDING);
+      CASE(SCTP_S_SHUTDOWN_SENT);
+      CASE(SCTP_S_SHUTDOWN_RECEIVED);
+      CASE(SCTP_S_SHUTDOWN_ACK_SENT);
+   }
+   return s;
 #undef CASE
 }
 
 const char* SCTPAssociation::eventName(const int32 event)
 {
 #define CASE(x) case x: s=#x+7; break
-    const char* s = "unknown";
-    switch (event) {
-        CASE(SCTP_E_OPEN_PASSIVE);
-        CASE(SCTP_E_ASSOCIATE);
-        CASE(SCTP_E_SHUTDOWN);
-        CASE(SCTP_E_CLOSE);
-        CASE(SCTP_E_ABORT);
-        CASE(SCTP_E_SEND);
-        CASE(SCTP_E_RCV_INIT);
-        CASE(SCTP_E_RCV_ABORT);
-        CASE(SCTP_E_RCV_VALID_COOKIE_ECHO);
-        CASE(SCTP_E_RCV_INIT_ACK);
-        CASE(SCTP_E_RCV_COOKIE_ACK);
-        CASE(SCTP_E_RCV_SHUTDOWN);
-        CASE(SCTP_E_RCV_SHUTDOWN_ACK);
-        CASE(SCTP_E_RCV_SHUTDOWN_COMPLETE);
-        CASE(SCTP_E_TIMEOUT_INIT_TIMER);
-        CASE(SCTP_E_TIMEOUT_SHUTDOWN_TIMER);
-        CASE(SCTP_E_TIMEOUT_RTX_TIMER);
-        CASE(SCTP_E_TIMEOUT_HEARTBEAT_TIMER);
-        CASE(SCTP_E_RECEIVE);
-        CASE(SCTP_E_DUP_RECEIVED);
-        CASE(SCTP_E_PRIMARY);
-        CASE(SCTP_E_QUEUE_MSGS_LIMIT);
-        CASE(SCTP_E_QUEUE_BYTES_LIMIT);
-        CASE(SCTP_E_NO_MORE_OUTSTANDING);
-        CASE(SCTP_E_IGNORE);
-        CASE(SCTP_E_DELIVERED);
-        CASE(SCTP_E_SEND_SHUTDOWN_ACK);
-        CASE(SCTP_E_STOP_SENDING);
-    }
-    return s;
+   const char* s = "unknown";
+   switch (event) {
+      CASE(SCTP_E_OPEN_PASSIVE);
+      CASE(SCTP_E_ASSOCIATE);
+      CASE(SCTP_E_SHUTDOWN);
+      CASE(SCTP_E_CLOSE);
+      CASE(SCTP_E_ABORT);
+      CASE(SCTP_E_SEND);
+      CASE(SCTP_E_RCV_INIT);
+      CASE(SCTP_E_RCV_ABORT);
+      CASE(SCTP_E_RCV_VALID_COOKIE_ECHO);
+      CASE(SCTP_E_RCV_INIT_ACK);
+      CASE(SCTP_E_RCV_COOKIE_ACK);
+      CASE(SCTP_E_RCV_SHUTDOWN);
+      CASE(SCTP_E_RCV_SHUTDOWN_ACK);
+      CASE(SCTP_E_RCV_SHUTDOWN_COMPLETE);
+      CASE(SCTP_E_TIMEOUT_INIT_TIMER);
+      CASE(SCTP_E_TIMEOUT_SHUTDOWN_TIMER);
+      CASE(SCTP_E_TIMEOUT_RTX_TIMER);
+      CASE(SCTP_E_TIMEOUT_HEARTBEAT_TIMER);
+      CASE(SCTP_E_RECEIVE);
+      CASE(SCTP_E_DUP_RECEIVED);
+      CASE(SCTP_E_PRIMARY);
+      CASE(SCTP_E_QUEUE_MSGS_LIMIT);
+      CASE(SCTP_E_QUEUE_BYTES_LIMIT);
+      CASE(SCTP_E_NO_MORE_OUTSTANDING);
+      CASE(SCTP_E_IGNORE);
+      CASE(SCTP_E_DELIVERED);
+      CASE(SCTP_E_SEND_SHUTDOWN_ACK);
+      CASE(SCTP_E_STOP_SENDING);
+      CASE(SCTP_E_STREAM_RESET);
+      CASE(SCTP_E_SEND_ASCONF);
+      CASE(SCTP_E_SET_STREAM_PRIO);
+   }
+   return s;
 #undef CASE
 }
 
 const char* SCTPAssociation::indicationName(const int32 code)
 {
 #define CASE(x) case x: s=#x+7; break
-    const char* s = "unknown";
-    switch (code) {
-        CASE(SCTP_I_DATA);
-        CASE(SCTP_I_DATA_NOTIFICATION);
-        CASE(SCTP_I_ESTABLISHED);
-        CASE(SCTP_I_PEER_CLOSED);
-        CASE(SCTP_I_CLOSED);
-        CASE(SCTP_I_CONNECTION_REFUSED);
-        CASE(SCTP_I_CONNECTION_RESET);
-        CASE(SCTP_I_TIMED_OUT);
-        CASE(SCTP_I_STATUS);
-        CASE(SCTP_I_ABORT);
-        CASE(SCTP_I_SHUTDOWN_RECEIVED);
-        CASE(SCTP_I_SEND_MSG);
-        CASE(SCTP_I_SENDQUEUE_FULL);
-        CASE(SCTP_I_SENDQUEUE_ABATED);
-    }
-    return s;
+   const char* s = "unknown";
+   switch (code) {
+      CASE(SCTP_I_DATA);
+      CASE(SCTP_I_DATA_NOTIFICATION);
+      CASE(SCTP_I_ESTABLISHED);
+      CASE(SCTP_I_PEER_CLOSED);
+      CASE(SCTP_I_CLOSED);
+      CASE(SCTP_I_CONNECTION_REFUSED);
+      CASE(SCTP_I_CONNECTION_RESET);
+      CASE(SCTP_I_TIMED_OUT);
+      CASE(SCTP_I_STATUS);
+      CASE(SCTP_I_ABORT);
+      CASE(SCTP_I_SHUTDOWN_RECEIVED);
+      CASE(SCTP_I_SEND_MSG);
+      CASE(SCTP_I_SENDQUEUE_FULL);
+      CASE(SCTP_I_SENDQUEUE_ABATED);
+      CASE(SCTP_I_ABANDONED);
+      CASE(SCTP_I_SEND_STREAMS_RESETTED);
+      CASE(SCTP_I_RCV_STREAMS_RESETTED);
+      CASE(SCTP_I_RESET_REQUEST_FAILED);
+      CASE(SCTP_I_ADDRESS_ADDED);
+   }
+   return s;
 #undef CASE
 }
 
-
-uint32 SCTPAssociation::chunkToInt(const char* type)
-{
-    if (strcmp(type, "DATA")==0) return 0;
-    if (strcmp(type, "INIT")==0) return 1;
-    if (strcmp(type, "INIT_ACK")==0) return 2;
-    if (strcmp(type, "SACK")==0) return 3;
-    if (strcmp(type, "HEARTBEAT")==0) return 4;
-    if (strcmp(type, "HEARTBEAT_ACK")==0) return 5;
-    if (strcmp(type, "ABORT")==0) return 6;
-    if (strcmp(type, "SHUTDOWN")==0) return 7;
-    if (strcmp(type, "SHUTDOWN_ACK")==0) return 8;
-    if (strcmp(type, "ERRORTYPE")==0) return 9;
-    if (strcmp(type, "COOKIE_ECHO")==0) return 10;
-    if (strcmp(type, "COOKIE_ACK")==0) return 11;
-    if (strcmp(type, "SHUTDOWN_COMPLETE")==0) return 14;
-    sctpEV3<<"ChunkConversion not successful\n";
-    return 0;
-}
-
-void SCTPAssociation::printConnBrief()
+void SCTPAssociation::printAssocBrief()
 {
-    sctpEV3 << "Connection " << this << " ";
-    sctpEV3 << localAddr << ":" << localPort << " to " << remoteAddr << ":" << remotePort;
-    sctpEV3 << "  on app[" << appGateIndex << "],assocId=" << assocId;
-    sctpEV3 << "  in " << stateName(fsm->getState()) << "\n";
+   sctpEV3 << "Connection " << this << " ";
+   sctpEV3 << localAddr << ":" << localPort << " to " << remoteAddr << ":" << remotePort;
+   sctpEV3 << "  on app[" << appGateIndex << "],assocId=" << assocId;
+   sctpEV3 << "  in " << stateName(fsm->getState()) << endl;
 }
 
 void SCTPAssociation::printSegmentBrief(SCTPMessage *sctpmsg)
 {
-    sctpEV3 << "." << sctpmsg->getSrcPort() << " > ";
-    sctpEV3 << "." << sctpmsg->getDestPort() << ": ";
-    sctpEV3 << "initTag "<< sctpmsg->getTag() << "\n";
+   sctpEV3 << "." << sctpmsg->getSrcPort() << " > ";
+   sctpEV3 << "." << sctpmsg->getDestPort() << ": ";
+   sctpEV3 << "initTag "<< sctpmsg->getTag() << endl;
 }
 
 SCTPAssociation* SCTPAssociation::cloneAssociation()
 {
-    SCTPAssociation* assoc = new SCTPAssociation(sctpMain,appGateIndex,assocId);
-    const char* queueClass = transmissionQ->getClassName();
-    assoc->transmissionQ = check_and_cast<SCTPQueue *>(createOne(queueClass));
-    assoc->retransmissionQ = check_and_cast<SCTPQueue *>(createOne(queueClass));
-
-    const char* sctpAlgorithmClass = sctpAlgorithm->getClassName();
-    assoc->sctpAlgorithm = check_and_cast<SCTPAlgorithm *>(createOne(sctpAlgorithmClass));
-    assoc->sctpAlgorithm->setAssociation(assoc);
-    assoc->sctpAlgorithm->initialize();
-    assoc->state = assoc->sctpAlgorithm->createStateVariables();
-
-    assoc->state->active = false;
-    assoc->state->fork = true;
-    assoc->localAddr = localAddr;
-    assoc->localPort = localPort;
-    assoc->localAddressList = localAddressList;
-
-    FSM_Goto((*assoc->fsm), SCTP_S_CLOSED);
-    sctpMain->printInfoConnMap();
-    return assoc;
-}
-
-void SCTPAssociation::recordInPathVectors(SCTPMessage* pMsg,
-                                          const IPvXAddress& rDest)
-{
-    uint32 n_chunks = pMsg->getChunksArraySize();
-    if (n_chunks == 0)
-       return;
-
-    SCTPPathVariables* p_path = getPath(rDest);
-
-    for (uint32 i = 0 ; i < n_chunks ; i++) {
-        const SCTPChunk* p_chunk = check_and_cast<const SCTPChunk *>(pMsg->getChunks(i));
-        if (p_chunk->getChunkType() == DATA) {
-            const SCTPDataChunk* p_data_chunk = check_and_cast<const SCTPDataChunk *>(p_chunk);
-            p_path->pathTSN->record(p_data_chunk->getTsn());
-        } else if (p_chunk->getChunkType() == HEARTBEAT) {
-            p_path->numberOfHeartbeatsSent++;
-            p_path->pathHb->record(p_path->numberOfHeartbeatsSent);
-        } else if (p_chunk->getChunkType() == HEARTBEAT_ACK) {
-            p_path->numberOfHeartbeatAcksSent++;
-            p_path->pathHbAck->record(p_path->numberOfHeartbeatAcksSent);
-        }
-    }
+   SCTPAssociation* assoc = new SCTPAssociation(sctpMain,appGateIndex,assocId);
+   const char* queueClass = transmissionQ->getClassName();
+   assoc->transmissionQ = check_and_cast<SCTPQueue *>(createOne(queueClass));
+   assoc->retransmissionQ = check_and_cast<SCTPQueue *>(createOne(queueClass));
+
+   const char* sctpAlgorithmClass = sctpAlgorithm->getClassName();
+   assoc->sctpAlgorithm = check_and_cast<SCTPAlgorithm *>(createOne(sctpAlgorithmClass));
+   assoc->sctpAlgorithm->setAssociation(assoc);
+   assoc->sctpAlgorithm->initialize();
+   assoc->state = assoc->sctpAlgorithm->createStateVariables();
+
+   assoc->state->active = false;
+   assoc->state->fork = true;
+   assoc->localAddr = localAddr;
+   assoc->localPort = localPort;
+   assoc->localAddressList = localAddressList;
+
+   assoc->outboundStreams = outboundStreams;
+   assoc->inboundStreams  = inboundStreams;
+
+   FSM_Goto((*assoc->fsm), SCTP_S_CLOSED);
+   sctpMain->printInfoAssocMap();
+   return assoc;
 }
 
 void SCTPAssociation::sendToIP(SCTPMessage*       sctpmsg,
-                                         const IPvXAddress& dest,
-                                         const bool           qs)
-{
-    // Final touches on the segment before sending
-    sctpmsg->setSrcPort(localPort);
-    sctpmsg->setDestPort(remotePort);
-    sctpmsg->setChecksumOk(true);
-    const SCTPChunk* chunk = (const SCTPChunk*)(sctpmsg->peekFirstChunk());
-    if (chunk->getChunkType() == ABORT) {
-        const SCTPAbortChunk* abortChunk = check_and_cast<const SCTPAbortChunk *>(chunk);
-        if (abortChunk->getT_Bit() == 1) {
-            sctpmsg->setTag(peerVTag);
-        }
-        else {
-            sctpmsg->setTag(localVTag);
-        }
-    }
-    else if (sctpmsg->getTag() == 0) {
-        sctpmsg->setTag(localVTag);
-    }
-
-    if ((bool)sctpMain->par("udpEncapsEnabled")) {
-        sctpmsg->setKind(UDP_C_DATA);
-        UDPControlInfo* controlInfo = new UDPControlInfo();
-        controlInfo->setSrcPort(9899);
-        controlInfo->setDestAddr(remoteAddr.get4());
-        controlInfo->setDestPort(9899);
-        sctpmsg->setControlInfo(controlInfo);
-    }
-    else {
-        if (dest.isIPv6()) {
-            IPv6ControlInfo* controlInfo = new IPv6ControlInfo();
-            controlInfo->setProtocol(IP_PROT_SCTP);
-            controlInfo->setSrcAddr(IPv6Address());
-            controlInfo->setDestAddr(dest.get6());
-            sctpmsg->setControlInfo(controlInfo);
-            sctpMain->send(sctpmsg, "to_ipv6");
-        }
-        else {
-            IPControlInfo* controlInfo = new IPControlInfo();
-            controlInfo->setProtocol(IP_PROT_SCTP);
-            controlInfo->setSrcAddr(IPAddress("0.0.0.0"));
-            controlInfo->setDestAddr(dest.get4());
-            sctpmsg->setControlInfo(controlInfo);
-            sctpMain->send(sctpmsg, "to_ip");
-        }
-        recordInPathVectors(sctpmsg, dest);
-    }
-    sctpEV3 << "Sent to " << dest << endl;
+                               const IPvXAddress& dest)
+{
+   // Final touches on the segment before sending
+   sctpmsg->setSrcPort(localPort);
+   sctpmsg->setDestPort(remotePort);
+   sctpmsg->setChecksumOk(true);
+   sctpEV3<<"SendToIP: localPort="<<localPort<<" remotePort="<<remotePort<<" dest="<<dest<<"\n";
+   const SCTPChunk* chunk = (const SCTPChunk*)(sctpmsg->peekFirstChunk());
+   if (chunk->getChunkType() == ABORT) {
+      const SCTPAbortChunk* abortChunk = check_and_cast<const SCTPAbortChunk *>(chunk);
+      if (abortChunk->getT_Bit() == 1) {
+         sctpmsg->setTag(peerVTag);
+      }
+      else {
+         sctpmsg->setTag(localVTag);
+      }
+   }
+   else if (sctpmsg->getTag() == 0) {
+      sctpmsg->setTag(localVTag);
+   }
+
+   if ((bool)sctpMain->par("udpEncapsEnabled")) {
+      sctpmsg->setKind(UDP_C_DATA);
+      UDPControlInfo* controlInfo = new UDPControlInfo();
+      controlInfo->setSrcPort(SCTP_OVER_UDP_UDPPORT);
+      controlInfo->setDestAddr(remoteAddr.get4());
+      controlInfo->setDestPort(SCTP_OVER_UDP_UDPPORT);
+      sctpmsg->setControlInfo(controlInfo);
+   }
+   else {
+      if (dest.isIPv6()) {
+         IPv6ControlInfo* controlInfo = new IPv6ControlInfo();
+         controlInfo->setProtocol(IP_PROT_SCTP);
+         controlInfo->setSrcAddr(IPv6Address());
+         controlInfo->setDestAddr(dest.get6());
+         sctpmsg->setControlInfo(controlInfo);
+         sctpMain->send(sctpmsg, "to_ipv6");
+      }
+      else {
+         IPControlInfo* controlInfo = new IPControlInfo();
+         controlInfo->setProtocol(IP_PROT_SCTP);
+         controlInfo->setSrcAddr(IPAddress("0.0.0.0"));
+         controlInfo->setDestAddr(dest.get4());
+         sctpmsg->setControlInfo(controlInfo);
+         sctpMain->send(sctpmsg, "to_ip");
+      }
+   }
+   sctpEV3 << "Sent to " << dest << endl;
 }
 
 
 void SCTPAssociation::signalConnectionTimeout()
 {
-    sendIndicationToApp(SCTP_I_TIMED_OUT);
+   sendIndicationToApp(SCTP_I_TIMED_OUT);
 }
 
 void SCTPAssociation::sendIndicationToApp(const int32 code, const int32 value)
 {
-    sctpEV3<<"sendIndicationToApp: " << indicationName(code) << endl;
-
-    cPacket* msg = new cPacket(indicationName(code));
-    msg->setKind(code);
-
-    SCTPCommand* indication = new SCTPCommand(indicationName(code));
-    indication->setAssocId(assocId);
-    indication->setLocalAddr(localAddr);
-    indication->setRemoteAddr(remoteAddr);
-    if (code == SCTP_I_SENDQUEUE_ABATED) {
-        indication->setNumMsgs(value);
-    }
-    msg->setControlInfo(indication);
-    sctpMain->send(msg, "to_appl", appGateIndex);
+   sctpEV3 << "sendIndicationToApp: " << indicationName(code) << endl;
+   assert(code != SCTP_I_SENDQUEUE_ABATED);
+
+   cPacket* msg = new cPacket(indicationName(code));
+   msg->setKind(code);
+
+   SCTPCommand* indication = new SCTPCommand(indicationName(code));
+   indication->setAssocId(assocId);
+   indication->setLocalAddr(localAddr);
+   indication->setRemoteAddr(remoteAddr);
+   msg->setControlInfo(indication);
+   sctpMain->send(msg, "to_appl", appGateIndex);
 }
 
 void SCTPAssociation::sendEstabIndicationToApp()
 {
-    sctpEV3 << "sendEstabIndicationToApp: localPort="
-              << localPort << " remotePort=" << remotePort << endl;
-
-    cPacket* msg = new cPacket(indicationName(SCTP_I_ESTABLISHED));
-    msg->setKind(SCTP_I_ESTABLISHED);
-
-    SCTPConnectInfo* establishIndication = new SCTPConnectInfo("CI");
-    establishIndication->setAssocId(assocId);
-    establishIndication->setLocalAddr(localAddr);
-    establishIndication->setRemoteAddr(remoteAddr);
-    establishIndication->setLocalPort(localPort);
-    establishIndication->setRemotePort(remotePort);
-    establishIndication->setRemoteAddresses(remoteAddressList);
-    establishIndication->setInboundStreams(inboundStreams);
-    establishIndication->setOutboundStreams(outboundStreams);
-    establishIndication->setNumMsgs(state->sendQueueLimit);
-    msg->setControlInfo(establishIndication);
-    sctpMain->send(msg, "to_appl", appGateIndex);
-
+   sctpEV3 << "sendEstabIndicationToApp: localPort="
+           << localPort << " remotePort=" << remotePort << endl;
+
+   cPacket* msg = new cPacket(indicationName(SCTP_I_ESTABLISHED));
+   msg->setKind(SCTP_I_ESTABLISHED);
+
+   SCTPConnectInfo* establishIndication = new SCTPConnectInfo("CI");
+   establishIndication->setAssocId(assocId);
+   establishIndication->setLocalAddr(localAddr);
+   establishIndication->setRemoteAddr(remoteAddr);
+   establishIndication->setLocalPort(localPort);
+   establishIndication->setRemotePort(remotePort);
+   establishIndication->setRemoteAddresses(remoteAddressList);
+   establishIndication->setInboundStreams(inboundStreams);
+   establishIndication->setOutboundStreams(outboundStreams);
+   establishIndication->setNumMsgs(state->sendQueueLimit);
+   msg->setControlInfo(establishIndication);
+   sctpMain->send(msg, "to_appl", appGateIndex);
+
+   char vectorName[128];
+   for (uint16 i = 0; i < inboundStreams; i++) {
+      snprintf(vectorName, sizeof(vectorName), "Stream %d Throughput", i);
+      streamThroughputVectors[i] = new cOutVector(vectorName);
+   }
 }
 
 void SCTPAssociation::sendToApp(cPacket *msg)
 {
-    sctpMain->send(msg, "to_appl", appGateIndex);
+   sctpMain->send(msg, "to_appl", appGateIndex);
 }
 
 void SCTPAssociation::initAssociation(SCTPOpenCommand *openCmd)
 {
-    sctpEV3<<"SCTPAssociationUtil:initAssociation\n";
-    // create send/receive queues
-    const char *queueClass = openCmd->getQueueClass();
-    transmissionQ = check_and_cast<SCTPQueue *>(createOne(queueClass));
-
-    retransmissionQ = check_and_cast<SCTPQueue *>(createOne(queueClass));
-    outboundStreams = openCmd->getOutboundStreams();
-    // create algorithm
-    const char *sctpAlgorithmClass = openCmd->getSctpAlgorithmClass();
-    if (!sctpAlgorithmClass || !sctpAlgorithmClass[0])
-        sctpAlgorithmClass = sctpMain->par("sctpAlgorithmClass");
-    sctpAlgorithm = check_and_cast<SCTPAlgorithm *>(createOne(sctpAlgorithmClass));
-    sctpAlgorithm->setAssociation(this);
-    sctpAlgorithm->initialize();
-    // create state block
-    state = sctpAlgorithm->createStateVariables();
+   sctpEV3<<"SCTPAssociationUtil:initAssociation\n";
+   // create send/receive queues
+   const char *queueClass = openCmd->getQueueClass();
+   transmissionQ = check_and_cast<SCTPQueue *>(createOne(queueClass));
+
+   retransmissionQ = check_and_cast<SCTPQueue *>(createOne(queueClass));
+   inboundStreams  = openCmd->getInboundStreams();
+   outboundStreams = openCmd->getOutboundStreams();
+   // create algorithm
+   const char *sctpAlgorithmClass = openCmd->getSctpAlgorithmClass();
+   if (!sctpAlgorithmClass || !sctpAlgorithmClass[0])
+      sctpAlgorithmClass = sctpMain->par("sctpAlgorithmClass");
+   sctpAlgorithm = check_and_cast<SCTPAlgorithm *>(createOne(sctpAlgorithmClass));
+   sctpAlgorithm->setAssociation(this);
+   sctpAlgorithm->initialize();
+   // create state block
+   state = sctpAlgorithm->createStateVariables();
+
+   char* token;
+   const char* chunks = sctpMain->par("chunks");
+   token = strtok((char*)chunks,",");
+   while (token != NULL)
+   {
+      this->state->chunkList.push_back(SCTP::chunkToInt(token));
+      sctpEV3<<"add "<<SCTP::chunkToInt(token)<<" to chunkList\n";
+      token = strtok(NULL, ",");
+   }
 }
 
 
 void SCTPAssociation::sendInit()
 {
-    //RoutingTableAccess routingTableAccess;
-    InterfaceTableAccess interfaceTableAccess;
-    AddressVector adv;
-    uint32 length = SCTP_INIT_CHUNK_LENGTH;
-
-    if (remoteAddr.isUnspecified() || remotePort==0)
-        opp_error("Error processing command ASSOCIATE: foreign socket unspecified");
-    if (localPort==0)
-        opp_error("Error processing command ASSOCIATE: local port unspecified");
-    state->setPrimaryPath(getPath(remoteAddr));
-    // create message consisting of INIT chunk
-    SCTPMessage *sctpmsg = new SCTPMessage();
-    sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
-    SCTPInitChunk *initChunk = new SCTPInitChunk("INIT");
-    initChunk->setChunkType(INIT);
-    initChunk->setInitTag((uint32)(fmod(intrand(INT32_MAX), 1.0+(double)(unsigned)0xffffffffUL)) & 0xffffffffUL);
-
-    peerVTag = initChunk->getInitTag();
-    sctpEV3<<"INIT from "<<localAddr<<":InitTag="<<peerVTag<<"\n";
-    initChunk->setA_rwnd(sctpMain->par("arwnd"));
-    state->localRwnd = (long)sctpMain->par("arwnd");
-    initChunk->setNoOutStreams(outboundStreams);
-    initChunk->setNoInStreams(inboundStreams);
-    initChunk->setInitTSN(1000);
-    state->nextTSN=initChunk->getInitTSN();
-    state->lastTSN = initChunk->getInitTSN() + state->numRequests - 1;
-    initTsn=initChunk->getInitTSN();
-    IInterfaceTable *ift = interfaceTableAccess.get();
-    sctpEV3<<"add local address\n";
-    if (localAddressList.front() == IPvXAddress("0.0.0.0"))
-    {
-        for (int32 i=0; i<ift->getNumInterfaces(); ++i)
-        {
-            if (ift->getInterface(i)->ipv4Data()!=NULL)
-            {
-                adv.push_back(ift->getInterface(i)->ipv4Data()->getIPAddress());
-            }
-            else if (ift->getInterface(i)->ipv6Data()!=NULL)
+   InterfaceTableAccess interfaceTableAccess;
+   AddressVector        adv;
+
+   uint32 length = SCTP_INIT_CHUNK_LENGTH;
+   if (remoteAddr.isUnspecified() || remotePort==0) {
+      opp_error("Error processing command ASSOCIATE: foreign socket unspecified");
+   }
+   if (localPort == 0) {
+      opp_error("Error processing command ASSOCIATE: local port unspecified");
+   }
+   state->setPrimaryPath(getPath(remoteAddr));
+
+   // create message consisting of INIT chunk
+   SCTPMessage *sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   SCTPInitChunk *initChunk = new SCTPInitChunk("INIT");
+   initChunk->setChunkType(INIT);
+   initChunk->setInitTag((uint32)(fmod(intrand(INT32_MAX), 1.0+(double)(unsigned)0xffffffffUL)) & 0xffffffffUL);
+
+   peerVTag = initChunk->getInitTag();
+   sctpEV3<<"INIT from "<<localAddr<<":InitTag="<<peerVTag<<"\n";
+   initChunk->setA_rwnd(sctpMain->par("arwnd"));
+   state->localRwnd = (long)sctpMain->par("arwnd");
+   initChunk->setNoOutStreams(outboundStreams);
+   initChunk->setNoInStreams(inboundStreams);
+   initChunk->setInitTSN(1000);
+   initChunk->setMsg_rwnd(sctpMain->par("messageAcceptLimit"));
+   state->nextTSN = initChunk->getInitTSN();
+   state->lastTSN = initChunk->getInitTSN() + state->numRequests - 1;
+   state->streamResetSequenceNumber = state->nextTSN;
+   state->asconfSn                  = 1000;
+
+   initTsn = initChunk->getInitTSN();
+
+   IInterfaceTable *ift = interfaceTableAccess.get();
+   sctpEV3<<"add local address\n";
+   if (localAddressList.front() == IPvXAddress("0.0.0.0"))
+   {
+      for (int32 i=0; i<ift->getNumInterfaces(); ++i)
+      {
+         if (ift->getInterface(i)->ipv4Data()!=NULL)
+         {
+            adv.push_back(ift->getInterface(i)->ipv4Data()->getIPAddress());
+         }
+         else if (ift->getInterface(i)->ipv6Data()!=NULL)
+         {
+            for (int32 j=0; j<ift->getInterface(i)->ipv6Data()->getNumAddresses(); j++)
             {
-                for (int32 j=0; j<ift->getInterface(i)->ipv6Data()->getNumAddresses(); j++)
-                {
-                    sctpEV3<<"add address "<<ift->getInterface(i)->ipv6Data()->getAddress(j)<<"\n";
-                    adv.push_back(ift->getInterface(i)->ipv6Data()->getAddress(j));
-                }
-            }
-        }
-    }
-    else
-    {
-        adv = localAddressList;
-        sctpEV3<<"gebundene Adresse "<<localAddr<<" wird hinzugefuegt\n";
-    }
-    uint32 addrNum=0;
-    bool friendly = false;
-    if (remoteAddr.isIPv6())
-    {
-        for (AddressVector::iterator i=adv.begin(); i!=adv.end(); ++i)
-        {
-            if (!friendly)
-            {
-                initChunk->setAddressesArraySize(addrNum+1);
-                initChunk->setAddresses(addrNum++,(*i));
-                length+=20;
+               sctpEV3<<"add address "<<ift->getInterface(i)->ipv6Data()->getAddress(j)<<"\n";
+               adv.push_back(ift->getInterface(i)->ipv6Data()->getAddress(j));
             }
+         }
+      }
+   }
+   else
+   {
+      adv = localAddressList;
+      sctpEV3<<"gebundene Adresse "<<localAddr<<" wird hinzugefuegt\n";
+   }
+
+   uint32 addrNum=0;
+   const bool friendly = sctpMain->par("natFriendly");
+   if (remoteAddr.isIPv6())
+   {
+      for (AddressVector::iterator i=adv.begin(); i!=adv.end(); ++i)
+      {
+         if (!friendly)
+         {
+            initChunk->setAddressesArraySize(addrNum+1);
+            initChunk->setAddresses(addrNum++,(*i));
+            length+=20;
+         }
+         sctpMain->addLocalAddress(this, (*i));
+         state->localAddresses.push_back((*i));
+         if (localAddr.isUnspecified())
+            localAddr=(*i);
+      }
+   }
+   else
+   {
+      uint32 rlevel = getLevel(remoteAddr);
+      sctpEV3<<"level of remote address="<<rlevel<<"\n";
+      for (AddressVector::iterator i=adv.begin(); i!=adv.end(); ++i)
+      {
+         sctpEV3<<"level of address "<<(*i)<<" = "<<getLevel((*i))<<"\n";
+         if (getLevel((*i))>=rlevel)
+         {
+            initChunk->setAddressesArraySize(addrNum+1);
+            initChunk->setAddresses(addrNum++,(*i));
+            length+=8;
             sctpMain->addLocalAddress(this, (*i));
             state->localAddresses.push_back((*i));
-            if (localAddr.isUnspecified())
-                localAddr=(*i);
-        }
-    }
-    else
-    {
-        uint32 rlevel = getLevel(remoteAddr);
-        sctpEV3<<"level of remote address="<<rlevel<<"\n";
-        for (AddressVector::iterator i=adv.begin(); i!=adv.end(); ++i)
-        {
-            sctpEV3<<"level of address "<<(*i)<<" = "<<getLevel((*i))<<"\n";
-            if (getLevel((*i))>=rlevel)
-            {
-                initChunk->setAddressesArraySize(addrNum+1);
-                initChunk->setAddresses(addrNum++,(*i));
-                length+=8;
-                sctpMain->addLocalAddress(this, (*i));
-                state->localAddresses.push_back((*i));
-                if (localAddr.get4().getInt()==0)
-                    localAddr=(*i);
-            }
-            else if (rlevel==4 && getLevel((*i))==3 && friendly)
-            {
-                sctpMain->addLocalAddress(this, (*i));
-                state->localAddresses.push_back((*i));
-                if (localAddr.get4().getInt()==0)
-                    localAddr=(*i);
-            }
-        }
-    }
-    sctpMain->printInfoConnMap();
-    initChunk->setBitLength(length*8);
-    sctpmsg->addChunk(initChunk);
-    // set path variables
-    if (remoteAddressList.size()>0)
-    {
-        for (AddressVector::iterator it=remoteAddressList.begin(); it!=remoteAddressList.end(); it++)
-        {
-            sctpEV3<<__LINE__<<" get new path for "<<(*it)<<"\n";
-            SCTPPathVariables* path = new SCTPPathVariables((*it), this);
-            sctpPathMap[(*it)] = path;
-            qCounter.roomTransQ[(*it)]    = 0;
-            qCounter.bookedTransQ[(*it)] = 0;
-            qCounter.roomRetransQ[(*it)] = 0;
-        }
-    }
-    else
-    {
-        sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<"\n";
-        SCTPPathVariables* path = new SCTPPathVariables(remoteAddr, this);
-        sctpPathMap[remoteAddr] = path;
-        qCounter.roomTransQ[remoteAddr]  = 0;
-        qCounter.bookedTransQ[remoteAddr] = 0;
-        qCounter.roomRetransQ[remoteAddr] = 0;
-    }
-    // send it
-    state->initChunk=check_and_cast<SCTPInitChunk *>(initChunk->dup());
-    state->initChunk->setName("StateInitChunk");
-    printSctpPathMap();
-    sctpEV3<<getFullPath()<<" sendInit: localVTag="<<localVTag<<" peerVTag="<<peerVTag<<"\n";
-    sendToIP(sctpmsg);
-    sctpMain->assocList.push_back(this);
+            if (localAddr.get4().getInt()==0)
+               localAddr=(*i);
+         }
+         else if (rlevel==4 && getLevel((*i))==3 && friendly)
+         {
+            sctpMain->addLocalAddress(this, (*i));
+            state->localAddresses.push_back((*i));
+            if (localAddr.get4().getInt()==0)
+               localAddr=(*i);
+         }
+      }
+   }
+
+   uint16 count = 0;
+   if (sctpMain->auth==true)
+   {
+      initChunk->setSepChunksArraySize(++count);
+      initChunk->setSepChunks(count-1, AUTH);
+      state->keyVector[0]=(uint8_t)RANDOM;
+      state->keyVector[2] = 36;
+      for (int32 k=0; k<32; k++)
+      {
+         initChunk->setRandomArraySize(k+1);
+         initChunk->setRandom(k, (uint8)(intrand(256)));
+         state->keyVector[k+2] = initChunk->getRandom(k);
+      }
+      state->sizeKeyVector = 36;
+      state->keyVector[state->sizeKeyVector] = (uint8_t)CHUNKS;
+      state->sizeKeyVector +=2;
+      state->keyVector[state->sizeKeyVector ] = state->chunkList.size()+4;
+      state->sizeKeyVector += 2;
+      initChunk->setChunkTypesArraySize(state->chunkList.size());
+      sctpEV3<<"noOfAuthChunks="<<initChunk->getChunkTypesArraySize()<<"\n";
+      int32 k=0;
+      for (std::vector<uint16>::iterator it=state->chunkList.begin(); it!=state->chunkList.end(); it++)
+      {
+         initChunk->setChunkTypes(k,(*it));
+         state->keyVector[state->sizeKeyVector] = (*it);
+         state->sizeKeyVector ++;
+         k++;
+      }
+      state->keyVector[state->sizeKeyVector] = (uint8_t)HMAC_ALGO;
+      state->sizeKeyVector += 2;
+      state->keyVector[state->sizeKeyVector] = 1+4;
+      state->sizeKeyVector += 2;
+      state->keyVector[state->sizeKeyVector] = 1;
+      state->sizeKeyVector ++;
+      initChunk->setHmacTypesArraySize(1);
+      initChunk->setHmacTypes(0,1);
+      length+=initChunk->getChunkTypesArraySize()+46;
+   }
+
+   if (sctpMain->pktdrop) {
+      initChunk->setSepChunksArraySize(++count);
+      initChunk->setSepChunks(count-1, PKTDROP);
+   }
+   if (state->streamReset == true) {
+      initChunk->setSepChunksArraySize(++count);
+      initChunk->setSepChunks(count-1, STREAM_RESET);
+   }
+   if((bool)sctpMain->par("addIP") == true) {
+      initChunk->setSepChunksArraySize(++count);
+      initChunk->setSepChunks(count-1, ASCONF);
+      initChunk->setSepChunksArraySize(++count);
+      initChunk->setSepChunks(count-1, ASCONF_ACK);
+   }
+   if (state->prMethod != 0) {
+      initChunk->setForwardTsn(true);
+   }
+
+   sctpMain->printInfoAssocMap();
+   initChunk->setBitLength(length*8);
+   sctpmsg->addChunk(initChunk);
+   // set path variables
+   if (remoteAddressList.size()>0)
+   {
+      for (AddressVector::iterator it=remoteAddressList.begin(); it!=remoteAddressList.end(); it++)
+      {
+         sctpEV3<<__LINE__<<" get new path for "<<(*it)<<"\n";
+         SCTPPathVariables* path = new SCTPPathVariables((*it), this);
+         sctpPathMap[(*it)] = path;
+         qCounter.roomTransQ[(*it)]   = 0;
+         qCounter.bookedTransQ[(*it)] = 0;
+         qCounter.roomRetransQ[(*it)] = 0;
+      }
+   }
+   else
+   {
+      sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<"\n";
+      SCTPPathVariables* path = new SCTPPathVariables(remoteAddr, this);
+      sctpPathMap[remoteAddr] = path;
+      qCounter.roomTransQ[remoteAddr]   = 0;
+      qCounter.bookedTransQ[remoteAddr] = 0;
+      qCounter.roomRetransQ[remoteAddr] = 0;
+   }
+   // send it
+   state->initChunk=check_and_cast<SCTPInitChunk *>(initChunk->dup());
+   state->initChunk->setName("StateInitChunk");
+   printSctpPathMap();
+   sctpEV3<<getFullPath()<<" sendInit: localVTag="<<localVTag<<" peerVTag="<<peerVTag<<"\n";
+   sendToIP(sctpmsg);
+   sctpMain->assocList.push_back(this);
 }
 
 void SCTPAssociation::retransmitInit()
 {
-    SCTPMessage *sctpmsg = new SCTPMessage();
-    sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
-    SCTPInitChunk *sctpinit;// = new SCTPInitChunk("INIT");
+   SCTPMessage *sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   SCTPInitChunk *sctpinit;// = new SCTPInitChunk("INIT");
 
-    sctpEV3<<"Retransmit InitChunk="<<&sctpinit<<"\n";
+   sctpEV3<<"Retransmit InitChunk="<<&sctpinit<<"\n";
 
-    sctpinit=check_and_cast<SCTPInitChunk *>(state->initChunk->dup());
-    sctpinit->setChunkType(INIT);
-    sctpmsg->addChunk(sctpinit);
+   sctpinit=check_and_cast<SCTPInitChunk *>(state->initChunk->dup());
+   sctpinit->setChunkType(INIT);
+   sctpmsg->addChunk(sctpinit);
 
-    sendToIP(sctpmsg);
+   sendToIP(sctpmsg);
 }
 
 
 void SCTPAssociation::sendInitAck(SCTPInitChunk* initChunk)
 {
-    uint32 length = SCTP_INIT_CHUNK_LENGTH;
-
-    state->setPrimaryPath(getPath(remoteAddr));
-    // create segment
-    SCTPMessage *sctpinitack = new SCTPMessage();
-    sctpinitack->setBitLength(SCTP_COMMON_HEADER*8);
-
-    sctpinitack->setSrcPort(localPort);
-    sctpinitack->setDestPort(remotePort);
-    sctpEV3<<"sendInitAck at "<<localAddr<<". Provided InitTag="<<initChunk->getInitTag()<<"\n";
-    SCTPInitAckChunk *initAckChunk = new SCTPInitAckChunk("INIT_ACK");
-    initAckChunk->setChunkType(INIT_ACK);
-    SCTPCookie *cookie = new SCTPCookie("CookieUtil");
-    cookie->setCreationTime(simTime());
-    cookie->setLocalTieTagArraySize(32);
-    cookie->setPeerTieTagArraySize(32);
-    if (fsm->getState()==SCTP_S_CLOSED)
-    {
-        while (peerVTag==0)
-        {
-            peerVTag = (uint32)intrand(INT32_MAX);
-        }
-        initAckChunk->setInitTag(peerVTag);
-        initAckChunk->setInitTSN(2000);
-        state->nextTSN=initAckChunk->getInitTSN();
-        state->lastTSN = initAckChunk->getInitTSN() + state->numRequests - 1;
-        cookie->setLocalTag(localVTag);
-        cookie->setPeerTag(peerVTag);
-        for (int32 i=0; i<32; i++)
-        {
+   uint32 length = SCTP_INIT_CHUNK_LENGTH;
+
+   state->setPrimaryPath(getPath(remoteAddr));
+   // create segment
+   SCTPMessage *sctpinitack = new SCTPMessage();
+   sctpinitack->setBitLength(SCTP_COMMON_HEADER*8);
+
+   sctpinitack->setSrcPort(localPort);
+   sctpinitack->setDestPort(remotePort);
+   sctpEV3<<"sendInitAck at "<<localAddr<<". Provided InitTag="<<initChunk->getInitTag()<<"\n";
+
+   SCTPInitAckChunk *initAckChunk = new SCTPInitAckChunk("INIT_ACK");
+   initAckChunk->setChunkType(INIT_ACK);
+
+   SCTPCookie *cookie = new SCTPCookie("CookieUtil");
+   cookie->setCreationTime(simTime());
+   cookie->setLocalTieTagArraySize(32);
+   cookie->setPeerTieTagArraySize(32);
+   if (fsm->getState()==SCTP_S_CLOSED)
+   {
+      while (peerVTag==0)
+      {
+         peerVTag = (uint32)intrand(INT32_MAX);
+      }
+      initAckChunk->setInitTag(peerVTag);
+      initAckChunk->setInitTSN(2000);
+      state->nextTSN=initAckChunk->getInitTSN();
+      state->lastTSN = initAckChunk->getInitTSN() + state->numRequests - 1;
+      state->asconfSn = 2000;
+      state->streamResetSequenceNumber = state->nextTSN;
+      cookie->setLocalTag(localVTag);
+      cookie->setPeerTag(peerVTag);
+      for (int32 i=0; i<32; i++)
+      {
+         cookie->setLocalTieTag(i,0);
+         cookie->setPeerTieTag(i,0);
+      }
+      sctpinitack->setTag(localVTag);
+      sctpEV3<<"state=closed: localVTag="<<localVTag<<" peerVTag="<<peerVTag<<"\n";
+   }
+   else if (fsm->getState()==SCTP_S_COOKIE_WAIT || fsm->getState()==SCTP_S_COOKIE_ECHOED)
+   {
+      initAckChunk->setInitTag(peerVTag);
+      sctpEV3<<"different state:set InitTag in InitAck: "<<initAckChunk->getInitTag()<<"\n";
+      initAckChunk->setInitTSN(state->nextTSN);
+      initPeerTsn=initChunk->getInitTSN();
+      state->gapList.forwardCumAckTSN(initPeerTsn - 1);
+      cookie->setLocalTag(initChunk->getInitTag());
+      cookie->setPeerTag(peerVTag);
+      for (int32 i=0; i<32; i++)
+      {
+         cookie->setPeerTieTag(i,(uint8)(intrand(256)));
+         state->peerTieTag[i] = cookie->getPeerTieTag(i);
+         if (fsm->getState()==SCTP_S_COOKIE_ECHOED)
+         {
+            cookie->setLocalTieTag(i,(uint8)(intrand(256)));
+            state->localTieTag[i] = cookie->getLocalTieTag(i);
+         }
+         else
             cookie->setLocalTieTag(i,0);
-            cookie->setPeerTieTag(i,0);
-        }
-        sctpinitack->setTag(localVTag);
-        sctpEV3<<"state=closed: localVTag="<<localVTag<<" peerVTag="<<peerVTag<<"\n";
-    }
-    else if (fsm->getState()==SCTP_S_COOKIE_WAIT || fsm->getState()==SCTP_S_COOKIE_ECHOED)
-    {
-        initAckChunk->setInitTag(peerVTag);
-        sctpEV3<<"different state:set InitTag in InitAck: "<<initAckChunk->getInitTag()<<"\n";
-        initAckChunk->setInitTSN(state->nextTSN);
-        initPeerTsn=initChunk->getInitTSN();
-        state->cTsnAck = initPeerTsn - 1;
-        cookie->setLocalTag(initChunk->getInitTag());
-        cookie->setPeerTag(peerVTag);
-        for (int32 i=0; i<32; i++)
-        {
-            cookie->setPeerTieTag(i,(uint8)(intrand(256)));
-            state->peerTieTag[i] = cookie->getPeerTieTag(i);
-            if (fsm->getState()==SCTP_S_COOKIE_ECHOED)
-            {
-                cookie->setLocalTieTag(i,(uint8)(intrand(256)));
-                state->localTieTag[i] = cookie->getLocalTieTag(i);
-            }
-            else
-                cookie->setLocalTieTag(i,0);
-        }
-        sctpinitack->setTag(initChunk->getInitTag());
-        sctpEV3<<"VTag in InitAck: "<<sctpinitack->getTag()<<"\n";
-    }
-    else
-    {
-        sctpEV3<<"other state\n";
-        uint32 tag=0;
-        while (tag==0)
-        {
-            tag = (uint32)(fmod(intrand(INT32_MAX), 1.0+(double)(unsigned)0xffffffffUL)) & 0xffffffffUL;
-        }
-        initAckChunk->setInitTag(tag);
-        initAckChunk->setInitTSN(state->nextTSN);
-        cookie->setLocalTag(localVTag);
-        cookie->setPeerTag(peerVTag);
-        for (int32 i=0; i<32; i++)
-        {
-            cookie->setPeerTieTag(i,state->peerTieTag[i]);
-            cookie->setLocalTieTag(i,state->localTieTag[i]);
-        }
-        sctpinitack->setTag(initChunk->getInitTag());
-    }
-    cookie->setBitLength(SCTP_COOKIE_LENGTH*8);
-    initAckChunk->setStateCookie(cookie);
-    initAckChunk->setCookieArraySize(0);
-    initAckChunk->setA_rwnd(sctpMain->par("arwnd"));
-    state->localRwnd = (long)sctpMain->par("arwnd");
-    initAckChunk->setNoOutStreams((unsigned int)min(outboundStreams,initChunk->getNoInStreams()));
-    initAckChunk->setNoInStreams((unsigned int)min(inboundStreams,initChunk->getNoOutStreams()));
-    initTsn=initAckChunk->getInitTSN();
-    uint32 addrNum=0;
-    bool friendly = false;
-    if (!friendly)
-    for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k)
-    {
-        initAckChunk->setAddressesArraySize(addrNum+1);
-        initAckChunk->setAddresses(addrNum++,(*k));
-        length+=8;
-    }
-    uint32 unknownLen = initChunk->getUnrecognizedParametersArraySize();
-    if (unknownLen>0)
-    {
-        sctpEV3<<"Found unrecognized Parameters in INIT chunk with a length of "<<unknownLen<<" bytes.\n";
-        initAckChunk->setUnrecognizedParametersArraySize(unknownLen);
-        for (uint32 i=0; i<unknownLen; i++)
-            initAckChunk->setUnrecognizedParameters(i,initChunk->getUnrecognizedParameters(i));
-        length+=unknownLen;
-    }
-    else
-        initAckChunk->setUnrecognizedParametersArraySize(0);
-
-    initAckChunk->setBitLength((length+initAckChunk->getCookieArraySize())*8 + cookie->getBitLength());
-    inboundStreams = ((initChunk->getNoOutStreams()<initAckChunk->getNoInStreams())?initChunk->getNoOutStreams():initAckChunk->getNoInStreams());
-    outboundStreams = ((initChunk->getNoInStreams()<initAckChunk->getNoOutStreams())?initChunk->getNoInStreams():initAckChunk->getNoOutStreams());
-    (this->*ssFunctions.ssInitStreams)(inboundStreams, outboundStreams);
-    sctpinitack->addChunk(initAckChunk);
-    if (fsm->getState()==SCTP_S_CLOSED)
-    {
-        sendToIP(sctpinitack, state->initialPrimaryPath);
-    }
-    else
-    {
-        sendToIP(sctpinitack);
-
-    }
-    sctpMain->assocList.push_back(this);
-    printSctpPathMap();
+      }
+      sctpinitack->setTag(initChunk->getInitTag());
+      sctpEV3<<"VTag in InitAck: "<<sctpinitack->getTag()<<"\n";
+   }
+   else
+   {
+      sctpEV3<<"other state\n";
+      uint32 tag=0;
+      while (tag==0)
+      {
+         tag = (uint32)(fmod(intrand(INT32_MAX), 1.0+(double)(unsigned)0xffffffffUL)) & 0xffffffffUL;
+      }
+      initAckChunk->setInitTag(tag);
+      initAckChunk->setInitTSN(state->nextTSN);
+      cookie->setLocalTag(localVTag);
+      cookie->setPeerTag(peerVTag);
+      for (int32 i=0; i<32; i++)
+      {
+         cookie->setPeerTieTag(i,state->peerTieTag[i]);
+         cookie->setLocalTieTag(i,state->localTieTag[i]);
+      }
+      sctpinitack->setTag(initChunk->getInitTag());
+   }
+   cookie->setBitLength(SCTP_COOKIE_LENGTH*8);
+
+   initAckChunk->setStateCookie(cookie);
+   initAckChunk->setCookieArraySize(0);
+   initAckChunk->setA_rwnd(sctpMain->par("arwnd"));
+   state->localRwnd = (long)sctpMain->par("arwnd");
+   initAckChunk->setMsg_rwnd(sctpMain->par("messageAcceptLimit"));
+   initAckChunk->setNoOutStreams((unsigned int)min(outboundStreams,initChunk->getNoInStreams()));
+   initAckChunk->setNoInStreams((unsigned int)min(inboundStreams,initChunk->getNoOutStreams()));
+
+   initTsn = initAckChunk->getInitTSN();
+   uint32 addrNum=0;
+   const bool friendly = sctpMain->par("natFriendly");
+   if (!friendly)
+   for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k)
+   {
+      initAckChunk->setAddressesArraySize(addrNum+1);
+      initAckChunk->setAddresses(addrNum++,(*k));
+      length+=8;
+   }
+
+   uint16 count = 0;
+   if (sctpMain->auth==true)
+   {
+      initAckChunk->setSepChunksArraySize(++count);
+      initAckChunk->setSepChunks(count-1, AUTH);
+      for (int32 k=0; k<32; k++)
+      {
+         initAckChunk->setRandomArraySize(k+1);
+         initAckChunk->setRandom(k, (uint8)(intrand(256)));
+      }
+      initAckChunk->setChunkTypesArraySize(state->chunkList.size());
+      int32 k=0;
+      for (std::vector<uint16>::iterator it=state->chunkList.begin(); it!=state->chunkList.end(); it++)
+      {
+         initAckChunk->setChunkTypes(k,(*it));
+         k++;
+      }
+      initAckChunk->setHmacTypesArraySize(1);
+      initAckChunk->setHmacTypes(0,1);
+      length+=initAckChunk->getChunkTypesArraySize()+46;
+
+   }
+
+   uint32 unknownLen = initChunk->getUnrecognizedParametersArraySize();
+   if (unknownLen>0)
+   {
+      sctpEV3<<"Found unrecognized Parameters in INIT chunk with a length of "<<unknownLen<<" bytes.\n";
+      initAckChunk->setUnrecognizedParametersArraySize(unknownLen);
+      for (uint32 i=0; i<unknownLen; i++)
+         initAckChunk->setUnrecognizedParameters(i,initChunk->getUnrecognizedParameters(i));
+      length+=unknownLen;
+   }
+   else
+      initAckChunk->setUnrecognizedParametersArraySize(0);
+
+   if (sctpMain->pktdrop)
+   {
+      initAckChunk->setSepChunksArraySize(++count);
+      initAckChunk->setSepChunks(count-1, PKTDROP);
+   }
+
+   if (state->streamReset== true)
+   {
+      initAckChunk->setSepChunksArraySize(++count);
+      initAckChunk->setSepChunks(count-1, STREAM_RESET);
+   }
+   if((bool)sctpMain->par("addIP") == true)
+   {
+      initAckChunk->setSepChunksArraySize(++count);
+      initAckChunk->setSepChunks(count-1, ASCONF);
+      initAckChunk->setSepChunksArraySize(++count);
+      initAckChunk->setSepChunks(count-1, ASCONF_ACK);
+   }
+   if (state->prMethod != 0)
+   {
+      initAckChunk->setForwardTsn(true);
+   }
+   length += count;
+
+   initAckChunk->setBitLength((length+initAckChunk->getCookieArraySize())*8 + cookie->getBitLength());
+   inboundStreams = ((initChunk->getNoOutStreams()<initAckChunk->getNoInStreams())?initChunk->getNoOutStreams():initAckChunk->getNoInStreams());
+   outboundStreams = ((initChunk->getNoInStreams()<initAckChunk->getNoOutStreams())?initChunk->getNoInStreams():initAckChunk->getNoOutStreams());
+   (this->*ssFunctions.ssInitStreams)(inboundStreams, outboundStreams);
+   sctpinitack->addChunk(initAckChunk);
+   if (fsm->getState()==SCTP_S_CLOSED)
+   {
+      sendToIP(sctpinitack, state->initialPrimaryPath);
+   }
+   else
+   {
+      sendToIP(sctpinitack);
+   }
+   sctpMain->assocList.push_back(this);
+   printSctpPathMap();
 }
 
 void SCTPAssociation::sendCookieEcho(SCTPInitAckChunk* initAckChunk)
 {
-    SCTPMessage *sctpcookieecho = new SCTPMessage();
-    sctpcookieecho->setBitLength(SCTP_COMMON_HEADER*8);
-
-    sctpEV3<<"SCTPAssociationUtil:sendCookieEcho\n";
-
-    sctpcookieecho->setSrcPort(localPort);
-    sctpcookieecho->setDestPort(remotePort);
-    SCTPCookieEchoChunk* cookieEchoChunk=new SCTPCookieEchoChunk("COOKIE_ECHO");
-    cookieEchoChunk->setChunkType(COOKIE_ECHO);
-    int32 len = initAckChunk->getCookieArraySize();
-    cookieEchoChunk->setCookieArraySize(len);
-    if (len>0)
-    {
-        for (int32 i=0; i<len; i++)
-            cookieEchoChunk->setCookie(i, initAckChunk->getCookie(i));
-        cookieEchoChunk->setBitLength((SCTP_COOKIE_ACK_LENGTH+len)*8);
-    }
-    else
-    {
-        SCTPCookie* cookie = check_and_cast <SCTPCookie*> (initAckChunk->getStateCookie());
-        cookieEchoChunk->setStateCookie(cookie);
-        cookieEchoChunk->setBitLength(SCTP_COOKIE_ACK_LENGTH*8 + cookie->getBitLength());
-    }
-    uint32 unknownLen = initAckChunk->getUnrecognizedParametersArraySize();
-    if (unknownLen>0)
-    {
-        sctpEV3<<"Found unrecognized Parameters in INIT-ACK chunk with a length of "<<unknownLen<<" bytes.\n";
-        cookieEchoChunk->setUnrecognizedParametersArraySize(unknownLen);
-        for (uint32 i=0; i<unknownLen; i++)
-            cookieEchoChunk->setUnrecognizedParameters(i,initAckChunk->getUnrecognizedParameters(i));
-    }
-    else
-        cookieEchoChunk->setUnrecognizedParametersArraySize(0);
-    state->cookieChunk=check_and_cast<SCTPCookieEchoChunk*>(cookieEchoChunk->dup());
-    if (len==0)
-    {
-        state->cookieChunk->setStateCookie(initAckChunk->getStateCookie()->dup());
-            }
-    sctpcookieecho->addChunk(cookieEchoChunk);
-        sendToIP(sctpcookieecho);
+   SCTPAuthenticationChunk* authChunk;
+   SCTPMessage *sctpcookieecho = new SCTPMessage();
+   sctpcookieecho->setBitLength(SCTP_COMMON_HEADER*8);
+
+   sctpEV3<<"SCTPAssociationUtil:sendCookieEcho\n";
+
+   sctpcookieecho->setSrcPort(localPort);
+   sctpcookieecho->setDestPort(remotePort);
+   SCTPCookieEchoChunk* cookieEchoChunk=new SCTPCookieEchoChunk("COOKIE_ECHO");
+   cookieEchoChunk->setChunkType(COOKIE_ECHO);
+   int32 len = initAckChunk->getCookieArraySize();
+   cookieEchoChunk->setCookieArraySize(len);
+   if (len>0)
+   {
+      for (int32 i=0; i<len; i++)
+         cookieEchoChunk->setCookie(i, initAckChunk->getCookie(i));
+      cookieEchoChunk->setBitLength((SCTP_COOKIE_ACK_LENGTH+len)*8);
+   }
+   else
+   {
+      SCTPCookie* cookie = check_and_cast <SCTPCookie*> (initAckChunk->getStateCookie());
+      cookieEchoChunk->setStateCookie(cookie);
+      cookieEchoChunk->setBitLength(SCTP_COOKIE_ACK_LENGTH*8 + cookie->getBitLength());
+   }
+   uint32 unknownLen = initAckChunk->getUnrecognizedParametersArraySize();
+   if (unknownLen>0)
+   {
+      sctpEV3<<"Found unrecognized Parameters in INIT-ACK chunk with a length of "<<unknownLen<<" bytes.\n";
+      cookieEchoChunk->setUnrecognizedParametersArraySize(unknownLen);
+      for (uint32 i=0; i<unknownLen; i++)
+         cookieEchoChunk->setUnrecognizedParameters(i,initAckChunk->getUnrecognizedParameters(i));
+   }
+   else
+      cookieEchoChunk->setUnrecognizedParametersArraySize(0);
+   state->cookieChunk=check_and_cast<SCTPCookieEchoChunk*>(cookieEchoChunk->dup());
+   if (len==0)
+   {
+      state->cookieChunk->setStateCookie(initAckChunk->getStateCookie()->dup());
+         }
+
+   if (state->auth && state->peerAuth && typeInChunkList(COOKIE_ECHO))
+   {
+      authChunk = createAuthChunk();
+      sctpcookieecho->addChunk(authChunk);
+   }
+
+   sctpcookieecho->addChunk(cookieEchoChunk);
+   sendToIP(sctpcookieecho);
 }
 
 
 void SCTPAssociation::retransmitCookieEcho()
 {
-    SCTPMessage*                 sctpmsg = new SCTPMessage();
-    sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
-    SCTPCookieEchoChunk* cookieEchoChunk=check_and_cast<SCTPCookieEchoChunk*>(state->cookieChunk->dup());
-    if (cookieEchoChunk->getCookieArraySize()==0)
-    {
-        cookieEchoChunk->setStateCookie(state->cookieChunk->getStateCookie()->dup());
-    }
-    sctpmsg->addChunk(cookieEchoChunk);
-
-    sctpEV3<<"retransmitCookieEcho localAddr="<<localAddr<<"     remoteAddr"<<remoteAddr<<"\n";
-
-    sendToIP(sctpmsg);
+   SCTPAuthenticationChunk* authChunk;
+   SCTPMessage*             sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   SCTPCookieEchoChunk* cookieEchoChunk=check_and_cast<SCTPCookieEchoChunk*>(state->cookieChunk->dup());
+   if (cookieEchoChunk->getCookieArraySize()==0)
+   {
+      cookieEchoChunk->setStateCookie(state->cookieChunk->getStateCookie()->dup());
+   }
+   if (state->auth && state->peerAuth && typeInChunkList(COOKIE_ECHO))
+   {
+      authChunk = createAuthChunk();
+      sctpmsg->addChunk(authChunk);
+   }
+   sctpmsg->addChunk(cookieEchoChunk);
+
+   sctpEV3<<"retransmitCookieEcho localAddr="<<localAddr<<"  remoteAddr"<<remoteAddr<<"\n";
+
+   sendToIP(sctpmsg);
 }
 
 void SCTPAssociation::sendHeartbeat(const SCTPPathVariables* path)
 {
-    SCTPMessage* sctpHeartbeatbeat = new SCTPMessage();
-    sctpHeartbeatbeat->setBitLength(SCTP_COMMON_HEADER*8);
-
-    sctpHeartbeatbeat->setSrcPort(localPort);
-    sctpHeartbeatbeat->setDestPort(remotePort);
-    SCTPHeartbeatChunk* heartbeatChunk = new SCTPHeartbeatChunk("HEARTBEAT");
-    heartbeatChunk->setChunkType(HEARTBEAT);
-    heartbeatChunk->setRemoteAddr(path->remoteAddress);
-    heartbeatChunk->setTimeField(simTime());
-    heartbeatChunk->setBitLength((SCTP_HEARTBEAT_CHUNK_LENGTH+12)*8);
-    sctpHeartbeatbeat->addChunk(heartbeatChunk);
-    sctpEV3 << "sendHeartbeat: sendToIP to " << path->remoteAddress << endl;
-    sendToIP(sctpHeartbeatbeat, path->remoteAddress);
+   SCTPAuthenticationChunk* authChunk;
+   SCTPMessage* sctpHeartbeatbeat = new SCTPMessage();
+   sctpHeartbeatbeat->setBitLength(SCTP_COMMON_HEADER*8);
+
+   sctpHeartbeatbeat->setSrcPort(localPort);
+   sctpHeartbeatbeat->setDestPort(remotePort);
+   SCTPHeartbeatChunk* heartbeatChunk = new SCTPHeartbeatChunk("HEARTBEAT");
+   heartbeatChunk->setChunkType(HEARTBEAT);
+   heartbeatChunk->setRemoteAddr(path->remoteAddress);
+   heartbeatChunk->setTimeField(simTime());
+   heartbeatChunk->setBitLength((SCTP_HEARTBEAT_CHUNK_LENGTH+12)*8);
+   if(state->auth && state->peerAuth && typeInChunkList(HEARTBEAT)) {
+      authChunk = createAuthChunk();
+      sctpHeartbeatbeat->addChunk(authChunk);
+   }
+   sctpHeartbeatbeat->addChunk(heartbeatChunk);
+   sctpEV3 << "sendHeartbeat: sendToIP to " << path->remoteAddress << endl;
+   sendToIP(sctpHeartbeatbeat, path->remoteAddress);
 }
 
 void SCTPAssociation::sendHeartbeatAck(const SCTPHeartbeatChunk* heartbeatChunk,
-                                                    const IPvXAddress&        src,
-                                                    const IPvXAddress&        dest)
-{
-    SCTPMessage*                 sctpHeartbeatAck = new SCTPMessage();
-    sctpHeartbeatAck->setBitLength(SCTP_COMMON_HEADER*8);
-    sctpHeartbeatAck->setSrcPort(localPort);
-    sctpHeartbeatAck->setDestPort(remotePort);
-    SCTPHeartbeatAckChunk* heartbeatAckChunk=new SCTPHeartbeatAckChunk("HEARTBEAT_ACK");
-    heartbeatAckChunk->setChunkType(HEARTBEAT_ACK);
-    heartbeatAckChunk->setRemoteAddr(heartbeatChunk->getRemoteAddr());
-    heartbeatAckChunk->setTimeField(heartbeatChunk->getTimeField());
-    const int32 len = heartbeatChunk->getInfoArraySize();
-    if (len > 0){
-        heartbeatAckChunk->setInfoArraySize(len);
-        for (int32 i=0; i<len; i++)
-            heartbeatAckChunk->setInfo(i,heartbeatChunk->getInfo(i));
-    }
-
-    heartbeatAckChunk->setBitLength(heartbeatChunk->getBitLength());
-    sctpHeartbeatAck->addChunk(heartbeatAckChunk);
-
-    sctpEV3 << "sendHeartbeatAck: sendToIP from " << src << " to " << dest << endl;
-    sendToIP(sctpHeartbeatAck, dest);
+                                       const IPvXAddress&        src,
+                                       const IPvXAddress&        dest)
+{
+   SCTPAuthenticationChunk* authChunk;
+   SCTPMessage*             sctpHeartbeatAck = new SCTPMessage();
+   sctpHeartbeatAck->setBitLength(SCTP_COMMON_HEADER*8);
+   sctpHeartbeatAck->setSrcPort(localPort);
+   sctpHeartbeatAck->setDestPort(remotePort);
+   SCTPHeartbeatAckChunk* heartbeatAckChunk=new SCTPHeartbeatAckChunk("HEARTBEAT_ACK");
+   heartbeatAckChunk->setChunkType(HEARTBEAT_ACK);
+   heartbeatAckChunk->setRemoteAddr(heartbeatChunk->getRemoteAddr());
+   heartbeatAckChunk->setTimeField(heartbeatChunk->getTimeField());
+   const int32 len = heartbeatChunk->getInfoArraySize();
+   if (len > 0){
+      heartbeatAckChunk->setInfoArraySize(len);
+      for (int32 i=0; i<len; i++)
+         heartbeatAckChunk->setInfo(i,heartbeatChunk->getInfo(i));
+   }
+
+   heartbeatAckChunk->setBitLength(heartbeatChunk->getBitLength());
+   if (state->auth && state->peerAuth && typeInChunkList(HEARTBEAT_ACK)) {
+      authChunk = createAuthChunk();
+      sctpHeartbeatAck->addChunk(authChunk);
+   }
+   sctpHeartbeatAck->addChunk(heartbeatAckChunk);
+
+   sctpEV3 << "sendHeartbeatAck: sendToIP from " << src << " to " << dest << endl;
+   sendToIP(sctpHeartbeatAck, dest);
 }
 
 void SCTPAssociation::sendCookieAck(const IPvXAddress& dest)
 {
-    SCTPMessage *sctpcookieack = new SCTPMessage();
-    sctpcookieack->setBitLength(SCTP_COMMON_HEADER*8);
-
-    sctpEV3<<"SCTPAssociationUtil:sendCookieACK\n";
-
-    sctpcookieack->setSrcPort(localPort);
-    sctpcookieack->setDestPort(remotePort);
-    SCTPCookieAckChunk* cookieAckChunk=new SCTPCookieAckChunk("COOKIE_ACK");
-    cookieAckChunk->setChunkType(COOKIE_ACK);
-    cookieAckChunk->setBitLength(SCTP_COOKIE_ACK_LENGTH*8);
-    sctpcookieack->addChunk(cookieAckChunk);
-    sendToIP(sctpcookieack, dest);
+   SCTPAuthenticationChunk* authChunk;
+   SCTPMessage *sctpcookieack = new SCTPMessage();
+   sctpcookieack->setBitLength(SCTP_COMMON_HEADER*8);
+
+   sctpEV3<<"SCTPAssociationUtil:sendCookieACK\n";
+
+   sctpcookieack->setSrcPort(localPort);
+   sctpcookieack->setDestPort(remotePort);
+   SCTPCookieAckChunk* cookieAckChunk=new SCTPCookieAckChunk("COOKIE_ACK");
+   cookieAckChunk->setChunkType(COOKIE_ACK);
+   cookieAckChunk->setBitLength(SCTP_COOKIE_ACK_LENGTH*8);
+   if (state->auth && state->peerAuth && typeInChunkList(COOKIE_ACK))
+   {
+      authChunk = createAuthChunk();
+      sctpcookieack->addChunk(authChunk);
+   }
+   sctpcookieack->addChunk(cookieAckChunk);
+   sendToIP(sctpcookieack, dest);
 }
 
 void SCTPAssociation::sendShutdownAck(const IPvXAddress& dest)
 {
-    sendOnAllPaths(getPath(dest));
-    if (getOutstandingBytes() == 0) {
-        performStateTransition(SCTP_E_NO_MORE_OUTSTANDING);
-        SCTPMessage *sctpshutdownack = new SCTPMessage();
-        sctpshutdownack->setBitLength(SCTP_COMMON_HEADER*8);
-
-        sctpEV3 << "SCTPAssociationUtil:sendShutdownACK" << endl;
-
-        sctpshutdownack->setSrcPort(localPort);
-        sctpshutdownack->setDestPort(remotePort);
-        SCTPShutdownAckChunk* shutdownAckChunk=new SCTPShutdownAckChunk("SHUTDOWN_ACK");
-        shutdownAckChunk->setChunkType(SHUTDOWN_ACK);
-        shutdownAckChunk->setBitLength(SCTP_COOKIE_ACK_LENGTH*8);
-        sctpshutdownack->addChunk(shutdownAckChunk);
-        state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT;
-        state->initRetransCounter = 0;
-        stopTimer(T2_ShutdownTimer);
-        startTimer(T2_ShutdownTimer,state->initRexmitTimeout);
-        stopTimer(T5_ShutdownGuardTimer);
-        startTimer(T5_ShutdownGuardTimer,SHUTDOWN_GUARD_TIMEOUT);
-        state->shutdownAckChunk=check_and_cast<SCTPShutdownAckChunk*>(shutdownAckChunk->dup());
-        sendToIP(sctpshutdownack, dest);
-    }
+   sendOnAllPaths(getPath(dest));
+   if (getOutstandingBytes() == 0) {
+      performStateTransition(SCTP_E_NO_MORE_OUTSTANDING);
+      SCTPMessage *sctpshutdownack = new SCTPMessage();
+      sctpshutdownack->setBitLength(SCTP_COMMON_HEADER*8);
+
+      sctpEV3 << "SCTPAssociationUtil:sendShutdownACK" << endl;
+
+      sctpshutdownack->setSrcPort(localPort);
+      sctpshutdownack->setDestPort(remotePort);
+      SCTPShutdownAckChunk* shutdownAckChunk=new SCTPShutdownAckChunk("SHUTDOWN_ACK");
+      shutdownAckChunk->setChunkType(SHUTDOWN_ACK);
+      shutdownAckChunk->setBitLength(SCTP_COOKIE_ACK_LENGTH*8);
+      sctpshutdownack->addChunk(shutdownAckChunk);
+      state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT;
+      state->initRetransCounter = 0;
+      stopTimer(T2_ShutdownTimer);
+      startTimer(T2_ShutdownTimer,state->initRexmitTimeout);
+      stopTimer(T5_ShutdownGuardTimer);
+      startTimer(T5_ShutdownGuardTimer,SHUTDOWN_GUARD_TIMEOUT);
+      state->shutdownAckChunk=check_and_cast<SCTPShutdownAckChunk*>(shutdownAckChunk->dup());
+      sendToIP(sctpshutdownack, dest);
+   }
 }
 
 void SCTPAssociation::sendShutdownComplete()
 {
-    SCTPMessage *sctpshutdowncomplete = new SCTPMessage();
-    sctpshutdowncomplete->setBitLength(SCTP_COMMON_HEADER*8);
-
-    sctpEV3<<"SCTPAssociationUtil:sendShutdownComplete\n";
-
-    sctpshutdowncomplete->setSrcPort(localPort);
-    sctpshutdowncomplete->setDestPort(remotePort);
-    SCTPShutdownCompleteChunk* shutdownCompleteChunk=new SCTPShutdownCompleteChunk("SHUTDOWN_COMPLETE");
-    shutdownCompleteChunk->setChunkType(SHUTDOWN_COMPLETE);
-    shutdownCompleteChunk->setTBit(0);
-    shutdownCompleteChunk->setBitLength(SCTP_SHUTDOWN_ACK_LENGTH*8);
-    sctpshutdowncomplete->addChunk(shutdownCompleteChunk);
-    sendToIP(sctpshutdowncomplete);
+   SCTPMessage *sctpshutdowncomplete = new SCTPMessage();
+   sctpshutdowncomplete->setBitLength(SCTP_COMMON_HEADER*8);
+
+   sctpEV3<<"SCTPAssociationUtil:sendShutdownComplete\n";
+
+   sctpshutdowncomplete->setSrcPort(localPort);
+   sctpshutdowncomplete->setDestPort(remotePort);
+   SCTPShutdownCompleteChunk* shutdownCompleteChunk=new SCTPShutdownCompleteChunk("SHUTDOWN_COMPLETE");
+   shutdownCompleteChunk->setChunkType(SHUTDOWN_COMPLETE);
+   shutdownCompleteChunk->setTBit(0);
+   shutdownCompleteChunk->setBitLength(SCTP_SHUTDOWN_ACK_LENGTH*8);
+   sctpshutdowncomplete->addChunk(shutdownCompleteChunk);
+   sendToIP(sctpshutdowncomplete);
 }
 
 
 void SCTPAssociation::sendAbort()
 {
-    SCTPMessage *msg = new SCTPMessage();
-    msg->setBitLength(SCTP_COMMON_HEADER*8);
-
-    sctpEV3<<"SCTPAssociationUtil:sendABORT localPort="<<localPort<<"    remotePort="<<remotePort<<"\n";
-
-    msg->setSrcPort(localPort);
-    msg->setDestPort(remotePort);
-    SCTPAbortChunk* abortChunk = new SCTPAbortChunk("ABORT");
-    abortChunk->setChunkType(ABORT);
-    abortChunk->setT_Bit(0);
-    abortChunk->setBitLength(SCTP_ABORT_CHUNK_LENGTH*8);
-    msg->addChunk(abortChunk);
-    sendToIP(msg, remoteAddr);
+   SCTPAuthenticationChunk* authChunk;
+   SCTPMessage *msg = new SCTPMessage();
+   msg->setBitLength(SCTP_COMMON_HEADER*8);
+
+   sctpEV3<<"SCTPAssociationUtil:sendABORT localPort="<<localPort<<"  remotePort="<<remotePort<<"\n";
+
+   msg->setSrcPort(localPort);
+   msg->setDestPort(remotePort);
+   SCTPAbortChunk* abortChunk = new SCTPAbortChunk("ABORT");
+   abortChunk->setChunkType(ABORT);
+   abortChunk->setT_Bit(0);
+   abortChunk->setBitLength(SCTP_ABORT_CHUNK_LENGTH*8);
+   if (state->auth && state->peerAuth && typeInChunkList(ABORT))
+   {
+      authChunk = createAuthChunk();
+      msg->addChunk(authChunk);
+   }
+   msg->addChunk(abortChunk);
+   sendToIP(msg, remoteAddr);
 }
 
 void SCTPAssociation::sendShutdown()
 {
-    SCTPMessage *msg = new SCTPMessage();
-    msg->setBitLength(SCTP_COMMON_HEADER*8);
-
-    sctpEV3<<"SCTPAssociationUtil:sendShutdown localPort="<<localPort<<"     remotePort="<<remotePort<<"\n";
-
-    msg->setSrcPort(localPort);
-    msg->setDestPort(remotePort);
-    SCTPShutdownChunk* shutdownChunk = new SCTPShutdownChunk("SHUTDOWN");
-    shutdownChunk->setChunkType(SHUTDOWN);
-    //shutdownChunk->setCumTsnAck(state->lastTsnAck);
-    shutdownChunk->setCumTsnAck(state->cTsnAck);
-    shutdownChunk->setBitLength(SCTP_SHUTDOWN_CHUNK_LENGTH*8);
-    state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT;
-    state->initRetransCounter = 0;
-    stopTimer(T5_ShutdownGuardTimer);
-    startTimer(T5_ShutdownGuardTimer,SHUTDOWN_GUARD_TIMEOUT);
-    stopTimer(T2_ShutdownTimer);
-    startTimer(T2_ShutdownTimer,state->initRexmitTimeout);
-    state->shutdownChunk=check_and_cast<SCTPShutdownChunk*>(shutdownChunk->dup());
-    msg->addChunk(shutdownChunk);
-    sendToIP(msg, remoteAddr);
-    performStateTransition(SCTP_E_NO_MORE_OUTSTANDING);
+   SCTPAuthenticationChunk* authChunk;
+   SCTPMessage *msg = new SCTPMessage();
+   msg->setBitLength(SCTP_COMMON_HEADER*8);
+
+   sctpEV3<<"SCTPAssociationUtil:sendShutdown localPort="<<localPort<<"  remotePort="<<remotePort<<"\n";
+
+   msg->setSrcPort(localPort);
+   msg->setDestPort(remotePort);
+   SCTPShutdownChunk* shutdownChunk = new SCTPShutdownChunk("SHUTDOWN");
+   shutdownChunk->setChunkType(SHUTDOWN);
+   //shutdownChunk->setCumTsnAck(state->lastTsnAck);
+   shutdownChunk->setCumTsnAck(state->gapList.getCumAckTSN());
+   shutdownChunk->setBitLength(SCTP_SHUTDOWN_CHUNK_LENGTH*8);
+   if (state->auth && state->peerAuth && typeInChunkList(SHUTDOWN))
+   {
+      authChunk = createAuthChunk();
+      msg->addChunk(authChunk);
+   }
+   state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT;
+   state->initRetransCounter = 0;
+   stopTimer(T5_ShutdownGuardTimer);
+   startTimer(T5_ShutdownGuardTimer,SHUTDOWN_GUARD_TIMEOUT);
+   stopTimer(T2_ShutdownTimer);
+   startTimer(T2_ShutdownTimer,state->initRexmitTimeout);
+   state->shutdownChunk=check_and_cast<SCTPShutdownChunk*>(shutdownChunk->dup());
+   msg->addChunk(shutdownChunk);
+   sendToIP(msg, remoteAddr);
+   performStateTransition(SCTP_E_NO_MORE_OUTSTANDING);
 }
 
 
 void SCTPAssociation::retransmitShutdown()
 {
-    SCTPMessage *sctpmsg = new SCTPMessage();
-    sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
-    SCTPShutdownChunk* shutdownChunk;
-    shutdownChunk=check_and_cast<SCTPShutdownChunk*>(state->shutdownChunk->dup());
-    sctpmsg->addChunk(shutdownChunk);
+   SCTPMessage *sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   SCTPShutdownChunk* shutdownChunk;
+   shutdownChunk=check_and_cast<SCTPShutdownChunk*>(state->shutdownChunk->dup());
+   sctpmsg->addChunk(shutdownChunk);
 
-    sctpEV3<<"retransmitShutdown localAddr="<<localAddr<<"  remoteAddr"<<remoteAddr<<"\n";
+   sctpEV3<<"retransmitShutdown localAddr="<<localAddr<<"  remoteAddr"<<remoteAddr<<"\n";
 
-    sendToIP(sctpmsg);
+   sendToIP(sctpmsg);
 }
 
 void SCTPAssociation::retransmitShutdownAck()
 {
-    SCTPMessage *sctpmsg = new SCTPMessage();
-    sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
-    SCTPShutdownAckChunk* shutdownAckChunk;
-    shutdownAckChunk=check_and_cast<SCTPShutdownAckChunk*>(state->shutdownAckChunk->dup());
-    sctpmsg->addChunk(shutdownAckChunk);
+   SCTPMessage *sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   SCTPShutdownAckChunk* shutdownAckChunk;
+   shutdownAckChunk=check_and_cast<SCTPShutdownAckChunk*>(state->shutdownAckChunk->dup());
+   sctpmsg->addChunk(shutdownAckChunk);
 
-    sctpEV3<<"retransmitShutdownAck localAddr="<<localAddr<<"  remoteAddr"<<remoteAddr<<"\n";
+   sctpEV3<<"retransmitShutdownAck localAddr="<<localAddr<<"  remoteAddr"<<remoteAddr<<"\n";
 
-    sendToIP(sctpmsg);
+   sendToIP(sctpmsg);
 }
 
+void SCTPAssociation::sendPacketDrop(const bool flag)
+{
+   sctpEV3 << "sendPacketDrop:\t";
+   SCTPMessage* drop = (SCTPMessage *)state->sctpmsg->dup();
+   if (drop->getChunksArraySize()==1)
+   {
+      SCTPChunk* header=(SCTPChunk*)(drop->getChunks(0));
+      if(header->getChunkType()==PKTDROP)
+      {
+         disposeOf(state->sctpmsg);
+         delete drop;
+         return;
+      }
+   }
+   SCTPMessage *sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   SCTPPacketDropChunk* pktdrop = new SCTPPacketDropChunk("PKTDROP");
+   pktdrop->setChunkType(PKTDROP);
+   pktdrop->setCFlag(false);
+   pktdrop->setTFlag(false);
+   pktdrop->setBFlag(flag);
+   pktdrop->setMFlag(false);
+   pktdrop->setMaxRwnd(sctpMain->par("arwnd"));
+   pktdrop->setQueuedData(state->queuedReceivedBytes);
+   pktdrop->setTruncLength(0);
+   pktdrop->setByteLength(SCTP_PKTDROP_CHUNK_LENGTH);
+   sctpEV3<<"length of msg to encapsulate="<<drop->getByteLength()<<"\n";
+   uint16 mss = getPath(remoteAddr)->pmtu - SCTP_COMMON_HEADER - SCTP_DATA_CHUNK_LENGTH - IP_HEADER_LENGTH;
+   if (drop->getByteLength()>mss)
+   {
+      uint16 diff = drop->getByteLength()-mss;
+      sctpEV3<<"msg has to be truncated\n";
+      pktdrop->setTruncLength(drop->getByteLength());
+      SCTPChunk* chunk=(SCTPChunk*)(drop->removeLastChunk());
+      if (chunk->getChunkType()==DATA)
+      {
+         SCTPDataChunk* dataChunk = check_and_cast<SCTPDataChunk*>(chunk);
+         SCTPSimpleMessage* smsg = check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate());
+         if (smsg->getDataLen() > diff)
+         {
+            uint16 newLength = smsg->getDataLen()-diff;
+            smsg->setDataArraySize(newLength);
+            for (uint16 i=0; i<newLength; i++)
+               smsg->setData(i, 'a');
+            smsg->setDataLen(newLength);
+            smsg->setEncaps(false);
+            smsg->setByteLength(newLength);
+            dataChunk->encapsulate(smsg);
+            drop->addChunk(dataChunk);
+         }
+         else if (drop->getChunksArraySize()==1)
+         {
+            delete chunk;
+            delete pktdrop;
+            disposeOf(state->sctpmsg);
+            disposeOf(drop);
+            sctpEV3<<"laenge="<<drop->getByteLength()<<" numberOfChunks=1\n";
+            return;
+         }
+      }
+      else
+      {
+         delete pktdrop;
+         disposeOf(state->sctpmsg);
+         disposeOf(drop);
+         sctpEV3<<"laenge="<<drop->getByteLength()<<" numberOfChunks=1\n";
+         return;
+      }
+      pktdrop->setTFlag(true);
+   }
+   pktdrop->encapsulate(drop);
+   sctpEV3<<"length of PKTDROP chunk="<<pktdrop->getBitLength()/8<<"\n";
+   sctpmsg->addChunk(pktdrop);
+   sctpEV3<<"total length now "<<sctpmsg->getByteLength()<<"\n";
+   disposeOf(state->sctpmsg);
+   state->pktDropSent = true;
+   sctpMain->numPktDropReports++;
+   sendToIP(sctpmsg);
+}
 
 void SCTPAssociation::scheduleSack()
 {
-    /* increase SACK counter, we received another data PACKET */
-    if (state->firstChunkReceived)
-        state->ackState++;
-    else
-    {
-        state->ackState = sackFrequency;
-        state->firstChunkReceived = true;
-    }
+   /* increase SACK counter, we received another data PACKET */
+   if (state->firstChunkReceived)
+      state->ackState++;
+   else
+   {
+      state->ackState = sackFrequency;
+      state->firstChunkReceived = true;
+   }
+
+   sctpEV3<<"scheduleSack() : ackState is now: "<<state->ackState<<"\n";
+
+   if (state->ackState <= sackFrequency - 1)
+   {
+      /* start a SACK timer if none is running, to expire 200 ms (or parameter) from now */
+      if (!SackTimer->isScheduled())
+      {
+          startTimer(SackTimer, sackPeriod);
+      }
+      /* else: leave timer running, and do nothing... */ else {
+         /* is this possible at all ? Check this... */
+
+         sctpEV3<<"SACK timer running, but scheduleSack() called\n";
+
+      }
+   }
+}
 
-    sctpEV3<<"scheduleSack() : ackState is now: "<<state->ackState<<"\n";
 
-    if (state->ackState <= sackFrequency - 1)
-    {
-        /* start a SACK timer if none is running, to expire 200 ms (or parameter) from now */
-        if (!SackTimer->isScheduled())
-        {
-             startTimer(SackTimer, sackPeriod);
-        }
-        /* else: leave timer running, and do nothing... */ else {
-            /* is this possible at all ? Check this... */
+SCTPForwardTsnChunk* SCTPAssociation::createForwardTsnChunk(const IPvXAddress& pid)
+{
+   uint16 chunkLength = SCTP_FORWARD_TSN_CHUNK_LENGTH;
+   SCTPDataVariables* chunk;
+   typedef std::map<uint16,int16> SidMap;
+   SidMap sidMap;
+
+   sctpEV3<<"Create forwardTsnChunk for "<<pid<<"\n";
+   SCTPForwardTsnChunk* forwChunk=new SCTPForwardTsnChunk("FORWARD_TSN");
+   forwChunk->setChunkType(FORWARD_TSN);
+   advancePeerTsn();
+   forwChunk->setNewCumTsn(state->advancedPeerAckPoint);
+   for(SCTPQueue::PayloadQueue::iterator it=retransmissionQ->payloadQueue.begin(); it!=retransmissionQ->payloadQueue.end(); it++)
+   {
+      chunk=it->second;
+      sctpEV3<<"tsn="<<chunk->tsn<<" lastDestination="<<chunk->getLastDestination()<<" abandoned="<<chunk->hasBeenAbandoned<<"\n";
+      if (chunk->getLastDestination() == pid && chunk->hasBeenAbandoned && chunk->tsn <= forwChunk->getNewCumTsn())
+      {
+         if (chunk->ordered)
+         {
+            sidMap[chunk->sid]=chunk->ssn;
+         }
+         else
+         {
+            sidMap[chunk->sid]=-1;
+         }
+         /* Fake chunk retransmission */
+         if (chunk->sendForwardIfAbandoned) {
+          chunk->gapReports = 0;
+          chunk->hasBeenFastRetransmitted = false;
+          chunk->sendTime = simTime();
+          chunk->numberOfRetransmissions++;
+            chunk->sendForwardIfAbandoned = false;
+
+            SCTPQueue::PayloadQueue::iterator itt=transmissionQ->payloadQueue.find(chunk->tsn);
+            if (itt != transmissionQ->payloadQueue.end()) {
+               transmissionQ->payloadQueue.erase(itt);
+               chunk->enqueuedInTransmissionQ = false;
+               CounterMap::iterator i = qCounter.roomTransQ.find(pid);
+               i->second -= ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH);
+               CounterMap::iterator ib = qCounter.bookedTransQ.find(pid);
+               ib->second -= chunk->booksize;
+            }
+         }
+      }
+   }
+   forwChunk->setSidArraySize(sidMap.size());
+   forwChunk->setSsnArraySize(sidMap.size());
+   int32 i=0;
+   for(SidMap::iterator j=sidMap.begin(); j!=sidMap.end(); j++)
+   {
+      forwChunk->setSid(i, j->first);
+      forwChunk->setSsn(i, j->second);
+      chunkLength+=4;
+      i++;
+   }
+   forwChunk->setByteLength(chunkLength);
+   SCTP::AssocStatMap::iterator iter=sctpMain->assocStatMap.find(assocId);
+      iter->second.numForwardTsn++;
+   return forwChunk;
+}
 
-            sctpEV3<<"SACK timer running, but scheduleSack() called\n";
 
-        }
-    }
-}
 
 
-SCTPSackChunk* SCTPAssociation::createSack()
+static uint32 copyToRGaps(SCTPSackChunk*         sackChunk,
+                          const GapList*         gapList,
+                          const GapList::GapType type,
+                          const bool             compression,
+                          size_t&                space)
+{
+   const uint32 count = gapList->getNumGaps(type);
+   uint32       last  = gapList->getCumAckTSN();
+   uint32       keys  = min(space / 4, count);   // Each entry occupies 2+2 bytes => at most space/4 entries
+
+   sackChunk->setGapStartArraySize(keys);
+   sackChunk->setGapStopArraySize(keys);
+   sackChunk->setNumGaps(keys);
+
+   for (uint32 key = 0; key < keys; key++) {
+      // ====== Validity check ========================================
+      assert(tsnGt(gapList->getGapStart(type, key), last + 1));
+      assert(tsnGe(gapList->getGapStop(type, key), gapList->getGapStart(type, key)));
+      sackChunk->setGapStart(key, gapList->getGapStart(type, key));
+      sackChunk->setGapStop(key, gapList->getGapStop(type, key));
+      last = gapList->getGapStop(type, key);
+   }
+      space = 4 * keys;
+
+   return(keys);
+}
+
+static uint32 copyToNRGaps(SCTPSackChunk*         sackChunk,
+                           const GapList*         gapList,
+                           const GapList::GapType type,
+                           const bool             compression,
+                           size_t&                space)
 {
-    uint32 key=0, arwnd=0;
+   const uint32 count = gapList->getNumGaps(type);
+   uint32       last  = gapList->getCumAckTSN();
+   uint32       keys  = min(space / 4, count);   // Each entry occupies 2+2 bytes => at most space/4 entries
+
+   sackChunk->setNrGapStartArraySize(keys);
+   sackChunk->setNrGapStopArraySize(keys);
+   sackChunk->setNumNrGaps(keys);
+
+   for (uint32 key = 0; key < keys; key++) {
+      // ====== Validity check ========================================
+      assert(tsnGt(gapList->getGapStart(type, key), last + 1));
+      assert(tsnGe(gapList->getGapStop(type, key), gapList->getGapStart(type, key)));
+      sackChunk->setNrGapStart(key, gapList->getGapStart(type, key));
+      sackChunk->setNrGapStop(key, gapList->getGapStop(type, key));
+      last = gapList->getGapStop(type, key);
+   }
+      space = 4 * keys;
+
+   return(keys);
+}
 
-    sctpEV3<<"SCTPAssociationUtil:createSACK localAddress="<<localAddr<<"  remoteAddress="<<remoteAddr<<"\n";
 
-    sctpEV3<<" localRwnd="<<state->localRwnd<<" queuedBytes="<<state->queuedReceivedBytes<<"\n";
-    if ((int32)(state->localRwnd - state->queuedReceivedBytes) <= 0)
-    {
-        arwnd = 0;
-        if (state->swsLimit > 0)
+SCTPSackChunk* SCTPAssociation::createSack()
+{
+   sctpEV3 << simTime() << ": createSack:"
+           << " localAddress="  << localAddr
+           << " remoteAddress=" << remoteAddr
+           << " localRwnd="     << state->localRwnd
+           << " queuedBytes="   << state->queuedReceivedBytes << endl;
+
+   // ====== Get receiver window size to be advertised ======================
+   uint32 arwnd   = 0;
+   uint32 msgRwnd = 0;
+   calculateRcvBuffer();
+   if ((state->messageAcceptLimit>0 && (int32)(state->localMsgRwnd - state->bufferedMessages) <= 0)
+      || (state->messageAcceptLimit==0 && (int32)(state->localRwnd - state->queuedReceivedBytes - state->bufferedMessages*state->bytesToAddPerRcvdChunk) <= 0))
+   {
+      msgRwnd = 0;
+   }
+   else if ((state->messageAcceptLimit>0 && (int32)(state->localMsgRwnd - state->bufferedMessages) < 3)
+      || (state->messageAcceptLimit==0 && state->localRwnd - state->queuedReceivedBytes - state->bufferedMessages*state->bytesToAddPerRcvdChunk < state->swsLimit) || state->swsMsgInvoked == true)
+   {
+      msgRwnd = 1;
+      state->swsMsgInvoked = true;
+   }
+   else {
+      if (state->messageAcceptLimit > 0) {
+         msgRwnd = state->localMsgRwnd - state->bufferedMessages;
+      }
+      else {
+         msgRwnd = state->localRwnd -
+                      state->queuedReceivedBytes -
+                      state->bufferedMessages*state->bytesToAddPerRcvdChunk;
+      }
+   }
+   if (state->tellArwnd) {
+      arwnd = msgRwnd;
+   }
+   else {
+      // ====== Receiver buffer is full =====================================
+      if ((int32)(state->localRwnd - state->queuedReceivedBytes) <= 0) {
+         arwnd = 0;
+         if (state->swsLimit > 0) {
             state->swsAvoidanceInvoked = true;
-    }
-    else if (state->localRwnd - state->queuedReceivedBytes < state->swsLimit || state->swsAvoidanceInvoked == true)
-    {
-        arwnd = 1;
-        if (state->swsLimit > 0)
+         }
+      }
+      // ====== Silly window syndrome avoidance =============================
+      else if ((state->localRwnd - state->queuedReceivedBytes < state->swsLimit) ||
+               (state->swsAvoidanceInvoked == true)) {
+         arwnd = 1;
+         if (state->swsLimit > 0) {
             state->swsAvoidanceInvoked = true;
-        sctpEV3<<"arwnd=1; createSack : SWS Avoidance ACTIVE !!!\n";
-    }
-    else
-    {
-        arwnd = state->localRwnd - state->queuedReceivedBytes;
-        sctpEV3<<simTime()<<" arwnd = "<<state->localRwnd<<" - "<<state->queuedReceivedBytes<<" = "<<arwnd<<"\n";
-    }
-    advRwnd->record(arwnd);
-    SCTPSackChunk* sackChunk=new SCTPSackChunk("SACK");
-    sackChunk->setChunkType(SACK);
-    sackChunk->setCumTsnAck(state->cTsnAck);
-    sackChunk->setA_rwnd(arwnd);
-    uint32 numGaps=state->numGaps;
-    uint32 numDups=state->dupList.size();
-    uint16 sackLength=SCTP_SACK_CHUNK_LENGTH + numGaps*4 + numDups*4;
-    uint32 mtu = getPath(remoteAddr)->pmtu;
-
-    if (sackLength > mtu-32) // FIXME
-    {
-        if (SCTP_SACK_CHUNK_LENGTH + numGaps*4 > mtu-32)
-        {
-            numDups = 0;
-            numGaps = (uint32)((mtu-32-SCTP_SACK_CHUNK_LENGTH)/4);
-        }
-        else
-        {
-            numDups = (uint32)((mtu-32-SCTP_SACK_CHUNK_LENGTH - numGaps*4)/4);
-        }
-        sackLength=SCTP_SACK_CHUNK_LENGTH + numGaps*4 + numDups*4;
-    }
-    sackChunk->setNumGaps(numGaps);
-    sackChunk->setNumDupTsns(numDups);
-    sackChunk->setBitLength(sackLength*8);
-
-    sctpEV3<<"Sack arwnd="<<sackChunk->getA_rwnd()<<" ctsnAck="<<state->cTsnAck<<" numGaps="<<numGaps<<" numDups="<<numDups<<"\n";
-
-    if (numGaps > 0)
-    {
-        sackChunk->setGapStartArraySize(numGaps);
-        sackChunk->setGapStopArraySize(numGaps);
-
-        uint32 last = state->cTsnAck;
-        for (key=0; key<numGaps; key++)
-        {
-            // ====== Validity check ===========================================
-            assert(tsnGt(state->gapStartList[key], last + 1));
-            assert(tsnGe(state->gapStopList[key], state->gapStartList[key]));
-            last = state->gapStopList[key];
-
-            sackChunk->setGapStart(key, state->gapStartList[key]);
-            sackChunk->setGapStop(key, state->gapStopList[key]);
-        }
-    }
-    if (numDups > 0)
-    {
-        sackChunk->setDupTsnsArraySize(numDups);
-        key=0;
-        for(std::list<uint32>::iterator iter=state->dupList.begin(); iter!=state->dupList.end(); iter++)
-        {
-            sackChunk->setDupTsns(key, (*iter));
-            key++;
-            if (key == numDups)
-                break;
-        }
-        state->dupList.clear();
-    }
-    sctpEV3<<endl;
-    for (uint32 i=0; i<numGaps; i++)
-        sctpEV3<<sackChunk->getGapStart(i)<<" - "<<sackChunk->getGapStop(i)<<"\n";
-
-    sctpEV3<<"send "<<sackChunk->getName()<<" from "<<localAddr<<" to "<<state->lastDataSourceAddress<<"\n";
-    return sackChunk;
+         }
+         sctpEV3 << "arwnd=1; createSack SWS Avoidance ACTIVE" << endl;
+      }
+      // ====== There is space in the receiver buffer =======================
+      else {
+         arwnd = state->localRwnd - state->queuedReceivedBytes;
+         sctpEV3 << "arwnd=" << state->localRwnd << " - " << state->queuedReceivedBytes
+                 << " = " << arwnd << endl;
+      }
+   }
+
+
+   // ====== Record statistics ==============================================
+   if (state->messageAcceptLimit > 0) {
+      advMsgRwnd->record(msgRwnd);
+   }
+   statisticsQueuedReceivedBytes->record(state->queuedReceivedBytes);
+   advRwnd->record(arwnd);
+
+
+   // ====== Create SACK chunk ==============================================
+   SCTPSackChunk* sackChunk = new SCTPSackChunk("SACK");
+   sackChunk->setChunkType(SACK);
+   sackChunk->setCumTsnAck(state->gapList.getCumAckTSN());
+   sackChunk->setA_rwnd(arwnd);
+   sackChunk->setIsNrSack(state->nrSack);
+   sackChunk->setSackSeqNum(++state->outgoingSackSeqNum);
+   if (state->messageAcceptLimit > 0) {
+      sackChunk->setMsg_rwnd(state->messageAcceptLimit-state->bufferedMessages);
+   }
+   else {
+      sackChunk->setMsg_rwnd(0);
+   }
+
+
+   // ====== What has to be stored in the SACK? =============================
+   const uint32 mtu             = getPath(remoteAddr)->pmtu;
+   const uint32 allowedLength   = mtu -
+                                     ((remoteAddr.isIPv6()) ? 40 : 20) -
+                                     SCTP_COMMON_HEADER -
+                                     SCTP_SACK_CHUNK_LENGTH;
+   uint32 numDups               = state->dupList.size();
+   uint32 numRevokableGaps      = state->gapList.getNumGaps(GapList::GT_Revokable);
+   uint32 numNonRevokableGaps   = state->gapList.getNumGaps(GapList::GT_NonRevokable);
+   size_t revokableGapsSpace    = ~0;
+   size_t nonRevokableGapsSpace = ~0;
+   size_t sackHeaderLength      = ~0;
+   const uint32 totalGaps       = state->gapList.getNumGaps(GapList::GT_Any);
+   bool         compression     = false;
+
+   // ====== Record statistics ==============================================
+   statisticsNumTotalGapBlocksStored->record(totalGaps);
+   statisticsNumRevokableGapBlocksStored->record(numRevokableGaps);
+   statisticsNumNonRevokableGapBlocksStored->record(numNonRevokableGaps);
+   statisticsNumDuplicatesStored->record(numDups);
+
+      // ------ Regular NR-SACK ---------------------------
+      if(state->nrSack == true) {
+         sackHeaderLength = SCTP_NRSACK_CHUNK_LENGTH;
+         numRevokableGaps    = copyToRGaps(sackChunk,  &state->gapList, GapList::GT_Revokable,    compression, revokableGapsSpace);    // Add R-acks only
+         numNonRevokableGaps = copyToNRGaps(sackChunk, &state->gapList, GapList::GT_NonRevokable, compression, nonRevokableGapsSpace); // Add NR-acks only
+      }
+      // ------ Regular SACK ------------------------------
+      else {
+         sackHeaderLength      = SCTP_SACK_CHUNK_LENGTH;
+         numRevokableGaps      = copyToRGaps(sackChunk, &state->gapList,  GapList::GT_Any, false, revokableGapsSpace);            // Add ALL
+         numNonRevokableGaps   = 0;
+         nonRevokableGapsSpace = 0;
+      }
+
+
+   // ====== SACK has to be shorted to fit in MTU ===========================
+   uint32 sackLength = sackHeaderLength + revokableGapsSpace + nonRevokableGapsSpace + numDups*4;
+   if (sackLength > allowedLength) {
+/*
+      std::cout << simTime() << "\t" << sackLength << " - " << allowedLength << "\t"
+                << "r="    << state->gapList.getNumGaps(GapList::GT_Revokable) << "\t"
+                << "nr="   << state->gapList.getNumGaps(GapList::GT_NonRevokable) << "\t"
+                << "ANY="  << state->gapList.getNumGaps(GapList::GT_Any) << "\t"
+                << "SUM="  << state->gapList.getNumGaps(GapList::GT_Revokable) + state->gapList.getNumGaps(GapList::GT_NonRevokable) << "\t"
+                << "r2="   << numRevokableGaps << "\t"
+                << "nr2="  << numNonRevokableGaps << "\t"
+                << "sum2=" << numRevokableGaps+numNonRevokableGaps << "\t"
+                << "dups=" << numDups << "\t"
+                << endl;
+*/
+
+      // T.D. 26.09.2010:  Strategy to reduce the SACK size:
+      // - Report no duplicates (they are not used for congestion control)
+      // - Split the remaining space equally between
+      //   revokable and non-revokable GapAcks
+
+      // ====== Drop duplicates list ========================================
+      numDups = 0;
+      sackLength -= 4 * numDups;
+
+      if(sackLength > allowedLength) {
+         // Unfortunately, dropping the duplicates has not solved the problem.
+         //    => Now, the gap lists have to be shortened!
+
+         SCTP::AssocStatMap::iterator iter=sctpMain->assocStatMap.find(assocId);
+         iter->second.numOverfullSACKs++;
+
+         revokableGapsSpace = allowedLength - sackHeaderLength;
+         if(totalGaps < (state->gapList.getNumGaps(GapList::GT_Revokable))) {
+            numRevokableGaps = copyToRGaps(sackChunk,  &state->gapList, GapList::GT_Any,          compression, revokableGapsSpace);    // Add ALL
+         }
+         else {
+            numRevokableGaps = copyToRGaps(sackChunk,  &state->gapList, GapList::GT_Revokable,    compression, revokableGapsSpace);    // Add R-acks only
+         }
+         sackLength = sackHeaderLength + revokableGapsSpace + nonRevokableGapsSpace + numDups*4;
+
+         // ====== Shorten gap lists ========================================
+            if(sackLength > allowedLength) {
+               const uint32 blocksBeRemoved   = (sackLength - allowedLength) / 4;
+               const double revokableFraction = numRevokableGaps / (double)(numRevokableGaps + numNonRevokableGaps);
+
+               const uint32 removeRevokable    = (uint32)ceil(blocksBeRemoved * revokableFraction);
+               const uint32 removeNonRevokable = (uint32)ceil(blocksBeRemoved * (1.0 - revokableFraction));
+               numRevokableGaps    -= std::min(removeRevokable, numRevokableGaps);
+               numNonRevokableGaps -= std::min(removeNonRevokable, numNonRevokableGaps);
+               revokableGapsSpace    = 4 * numRevokableGaps;
+               nonRevokableGapsSpace = 4 * numNonRevokableGaps;
+               numRevokableGaps    = copyToRGaps(sackChunk,  &state->gapList, GapList::GT_Revokable,    compression, revokableGapsSpace);    // Add R-acks only
+               numNonRevokableGaps = copyToNRGaps(sackChunk, &state->gapList, GapList::GT_NonRevokable, compression, nonRevokableGapsSpace); // Add NR-acks only
+               sackLength = sackHeaderLength + revokableGapsSpace + nonRevokableGapsSpace + numDups*4;
+            }
+         assert(sackLength <= allowedLength);
+
+         // Update values in SACK chunk ...
+         sackChunk->setNumGaps(numRevokableGaps);
+         sackChunk->setNumNrGaps(numNonRevokableGaps);
+      }
+   }
+   sackChunk->setNumDupTsns(numDups);
+   sackChunk->setBitLength(sackLength * 8);
+
+
+   // ====== Add duplicates =================================================
+   if (numDups > 0) {
+      sackChunk->setDupTsnsArraySize(numDups);
+      uint32 key = 0;
+      for(std::list<uint32>::iterator iterator = state->dupList.begin();
+          iterator != state->dupList.end(); iterator++) {
+         sackChunk->setDupTsns(key, *iterator);
+         key++;
+         if (key == numDups) {
+            break;
+         }
+      }
+      state->dupList.clear();
+   }
+
+   // ====== Record statistics ==============================================
+   statisticsSACKLengthSent->record(sackLength);
+   statisticsNumRevokableGapBlocksSent->record(numRevokableGaps);
+   statisticsNumNonRevokableGapBlocksSent->record(numNonRevokableGaps);
+   statisticsNumDuplicatesSent->record(numDups);
+
+   // ====== Print information ==============================================
+   if(SCTP::testing == true) {
+      sctpEV3 << "createSack:"
+              << " bufferedMessages=" << state->bufferedMessages
+              << " msgRwnd="          << msgRwnd
+              << " arwnd="            << sackChunk->getA_rwnd()
+              << " cumAck="           << state->gapList.getCumAckTSN()
+              << " numRGaps="         << numRevokableGaps
+              << " numNRGaps="        << numNonRevokableGaps
+              << " numDups="          << numDups
+              << endl;
+      state->gapList.print(sctpEV3);
+   }
+   return sackChunk;
 }
 
 void SCTPAssociation::sendSack()
 {
-    SCTPSackChunk*               sackChunk;
+   SCTPAuthenticationChunk* authChunk;
+   SCTPSackChunk*           sackChunk;
 
-    sctpEV3 << "Sending SACK" << endl;
+   sctpEV3 << "Sending SACK" << endl;
 
-    /* sack timer has expired, reset flag, and send SACK */
-    stopTimer(SackTimer);
-    state->ackState = 0;
-    sackChunk = createSack();
+   /* sack timer has expired, reset flag, and send SACK */
+   stopTimer(SackTimer);
+   state->ackState = 0;
+   sackChunk = createSack();
 
-    SCTPMessage* sctpmsg = new SCTPMessage();
-    sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
-    sctpmsg->addChunk(sackChunk);
+   SCTPMessage* sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   if (state->auth && state->peerAuth && typeInChunkList(SACK)) {
+      authChunk = createAuthChunk();
+      sctpmsg->addChunk(authChunk);
+   }
+   sctpmsg->addChunk(sackChunk);
 
-    // Return the SACK to the address where we last got a data chunk from
-    sendToIP(sctpmsg, state->lastDataSourceAddress);
+   sendSACKviaSelectedPath(sctpmsg);
 }
 
-void SCTPAssociation::sendDataArrivedNotification(uint16 sid)
+void SCTPAssociation::sendDataArrivedNotification(const uint16 sid)
 {
 
-    sctpEV3<<"SendDataArrivedNotification\n";
+   sctpEV3<<"SendDataArrivedNotification\n";
 
-    cPacket* cmsg = new cPacket("DataArrivedNotification");
-    cmsg->setKind(SCTP_I_DATA_NOTIFICATION);
-    SCTPCommand *cmd = new SCTPCommand("notification");
-    cmd->setAssocId(assocId);
-    cmd->setSid(sid);
-    cmd->setNumMsgs(1);
-    cmsg->setControlInfo(cmd);
+   cPacket* cmsg = new cPacket("DataArrivedNotification");
+   cmsg->setKind(SCTP_I_DATA_NOTIFICATION);
+   SCTPCommand *cmd = new SCTPCommand("notification");
+   cmd->setAssocId(assocId);
+   cmd->setSid(sid);
+   cmd->setNumMsgs(1);
+   cmsg->setControlInfo(cmd);
 
-    sendToApp(cmsg);
+   sendToApp(cmsg);
 }
 
-
-void SCTPAssociation::putInDeliveryQ(uint16 sid)
+void SCTPAssociation::sendHMacError(const uint16 id)
 {
-    SCTPReceiveStreamMap::iterator iter=receiveStreams.find(sid);
-    SCTPReceiveStream* rStream = iter->second;
-    sctpEV3 << "putInDeliveryQ: SSN=" << rStream->getExpectedStreamSeqNum()
-              << " SID=" << sid
-              << " QueueSize="<< rStream->getOrderedQ()->getQueueSize() << endl;
-    while (rStream->getOrderedQ()->getQueueSize()>0)
-    {
-        /* dequeue first from reassembly Q */
-        SCTPDataVariables* chunk =
-            rStream->getOrderedQ()-> dequeueChunkBySSN(rStream->getExpectedStreamSeqNum());
-        if (chunk) {
-            sctpEV3 << "putInDeliveryQ::chunk " <<chunk->tsn
-                      <<", sid " << chunk->sid <<" and ssn " << chunk->ssn
-                      <<" dequeued from ordered queue. queuedReceivedBytes="
-                      << state->queuedReceivedBytes << " will be reduced by "
-                      << chunk->len/8 << endl;
-            state->queuedReceivedBytes-=chunk->len/8;
-
+   SCTPMessage *sctpmsg = new SCTPMessage();
+   sctpmsg->setBitLength(SCTP_COMMON_HEADER*8);
+   SCTPErrorChunk* errorChunk = new SCTPErrorChunk("Error");
+   errorChunk->setChunkType(ERRORTYPE);
+   SCTPSimpleErrorCauseParameter* cause = new SCTPSimpleErrorCauseParameter("Cause");
+   cause->setParameterType(UNSUPPORTED_HMAC);
+   cause->setBitLength(6*8);
+   cause->setValue(id);
+   errorChunk->setBitLength(4*8);
+   errorChunk->addParameters(cause);
+   sctpmsg->addChunk(errorChunk);
+}
 
-            qCounter.roomSumRcvStreams -= ADD_PADDING(chunk->len/8 + SCTP_DATA_CHUNK_LENGTH);
-            if (rStream->getDeliveryQ()->checkAndInsertChunk(chunk->tsn, chunk)) {
-                state->queuedReceivedBytes += chunk->len/8;
-
-                sctpEV3 << "data put in deliveryQ; queuedBytes now "
-                          << state->queuedReceivedBytes << endl;
-                qCounter.roomSumRcvStreams += ADD_PADDING(chunk->len/8 + SCTP_DATA_CHUNK_LENGTH);
-                int32 seqnum=rStream->getExpectedStreamSeqNum();
-                rStream->setExpectedStreamSeqNum(++seqnum);
-                if (rStream->getExpectedStreamSeqNum() > 65535) {
-                    rStream->setExpectedStreamSeqNum(0);
-                }
-                sendDataArrivedNotification(sid);
+void SCTPAssociation::putInDeliveryQ(const uint16 sid)
+{
+   SCTPReceiveStream* rStream = receiveStreams.find(sid)->second;
+   sctpEV3 << "putInDeliveryQ: SSN=" << rStream->getExpectedStreamSeqNum()
+           << " SID=" << sid
+           << " QueueSize="<< rStream->getOrderedQ()->getQueueSize() << endl;
+   while (rStream->getOrderedQ()->getQueueSize() > 0) {
+      /* dequeue first from reassembly Q */
+      SCTPDataVariables* chunk =
+         rStream->getOrderedQ()-> dequeueChunkBySSN(rStream->getExpectedStreamSeqNum());
+      if (chunk) {
+         sctpEV3 << "putInDeliveryQ::chunk " <<chunk->tsn
+                 << ", sid " << chunk->sid <<" and ssn " << chunk->ssn
+                 << " dequeued from ordered queue. queuedReceivedBytes="
+                 << state->queuedReceivedBytes << " will be reduced by "
+                 << chunk->len/8 << endl;
+         state->bufferedMessages--;
+         state->queuedReceivedBytes -= chunk->len/8;
+         qCounter.roomSumRcvStreams -= ADD_PADDING(chunk->len/8 + SCTP_DATA_CHUNK_LENGTH);
+
+         if (rStream->getDeliveryQ()->checkAndInsertChunk(chunk->tsn, chunk)) {
+            state->bufferedMessages++;
+            state->queuedReceivedBytes += chunk->len/8;
+
+            sctpEV3 << "data put in deliveryQ; queuedBytes now "
+                    << state->queuedReceivedBytes << endl;
+            qCounter.roomSumRcvStreams += ADD_