First code commit
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3534f2f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,277 @@
+Eclipse Public License - v 2.0
+
+ THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+ PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+ OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+ a) in the case of the initial Contributor, the initial content
+ Distributed under this Agreement, and
+
+ b) in the case of each subsequent Contributor:
+ i) changes to the Program, and
+ ii) additions to the Program;
+ where such changes and/or additions to the Program originate from
+ and are Distributed by that particular Contributor. A Contribution
+ "originates" from a Contributor if it was added to the Program by
+ such Contributor itself or anyone acting on such Contributor's behalf.
+ Contributions do not include changes or additions to the Program that
+ are not Modified Works.
+
+"Contributor" means any person or entity that Distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions Distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement
+or any Secondary License (as applicable), including Contributors.
+
+"Derivative Works" shall mean any work, whether in Source Code or other
+form, that is based on (or derived from) the Program and for which the
+editorial revisions, annotations, elaborations, or other modifications
+represent, as a whole, an original work of authorship.
+
+"Modified Works" shall mean any work in Source Code or other form that
+results from an addition to, deletion from, or modification of the
+contents of the Program, including, for purposes of clarity any new file
+in Source Code form that contains any contents of the Program. Modified
+Works shall not include works that contain only declarations,
+interfaces, types, classes, structures, or files of the Program solely
+in each case in order to link to, bind by name, or subclass the Program
+or Modified Works thereof.
+
+"Distribute" means the acts of a) distributing or b) making available
+in any manner that enables the transfer of a copy.
+
+"Source Code" means the form of a Program preferred for making
+modifications, including but not limited to software source code,
+documentation source, and configuration files.
+
+"Secondary License" means either the GNU General Public License,
+Version 2.0, or any later versions of that license, including any
+exceptions or additional permissions as identified by the initial
+Contributor.
+
+2. GRANT OF RIGHTS
+
+ a) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare Derivative Works of, publicly display,
+ publicly perform, Distribute and sublicense the Contribution of such
+ Contributor, if any, and such Derivative Works.
+
+ b) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor,
+ if any, in Source Code or other form. This patent license shall
+ apply to the combination of the Contribution and the Program if, at
+ the time the Contribution is added by the Contributor, such addition
+ of the Contribution causes such combination to be covered by the
+ Licensed Patents. The patent license shall not apply to any other
+ combinations which include the Contribution. No hardware per se is
+ licensed hereunder.
+
+ c) Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe the
+ patent or other intellectual property rights of any other entity.
+ Each Contributor disclaims any liability to Recipient for claims
+ brought by any other entity based on infringement of intellectual
+ property rights or otherwise. As a condition to exercising the
+ rights and licenses granted hereunder, each Recipient hereby
+ assumes sole responsibility to secure any other intellectual
+ property rights needed, if any. For example, if a third party
+ patent license is required to allow Recipient to Distribute the
+ Program, it is Recipient's responsibility to acquire that license
+ before distributing the Program.
+
+ d) Each Contributor represents that to its knowledge it has
+ sufficient copyright rights in its Contribution, if any, to grant
+ the copyright license set forth in this Agreement.
+
+ e) Notwithstanding the terms of any Secondary License, no
+ Contributor makes additional grants to any Recipient (other than
+ those set forth in this Agreement) as a result of such Recipient's
+ receipt of the Program under the terms of a Secondary License
+ (if permitted under the terms of Section 3).
+
+3. REQUIREMENTS
+
+3.1 If a Contributor Distributes the Program in any form, then:
+
+ a) the Program must also be made available as Source Code, in
+ accordance with section 3.2, and the Contributor must accompany
+ the Program with a statement that the Source Code for the Program
+ is available under this Agreement, and informs Recipients how to
+ obtain it in a reasonable manner on or through a medium customarily
+ used for software exchange; and
+
+ b) the Contributor may Distribute the Program under a license
+ different than this Agreement, provided that such license:
+ i) effectively disclaims on behalf of all other Contributors all
+ warranties and conditions, express and implied, including
+ warranties or conditions of title and non-infringement, and
+ implied warranties or conditions of merchantability and fitness
+ for a particular purpose;
+
+ ii) effectively excludes on behalf of all other Contributors all
+ liability for damages, including direct, indirect, special,
+ incidental and consequential damages, such as lost profits;
+
+ iii) does not attempt to limit or alter the recipients' rights
+ in the Source Code under section 3.2; and
+
+ iv) requires any subsequent distribution of the Program by any
+ party to be under a license that satisfies the requirements
+ of this section 3.
+
+3.2 When the Program is Distributed as Source Code:
+
+ a) it must be made available under this Agreement, or if the
+ Program (i) is combined with other material in a separate file or
+ files made available under a Secondary License, and (ii) the initial
+ Contributor attached to the Source Code the notice described in
+ Exhibit A of this Agreement, then the Program may be made available
+ under the terms of such Secondary Licenses, and
+
+ b) a copy of this Agreement must be included with each copy of
+ the Program.
+
+3.3 Contributors may not remove or alter any copyright, patent,
+trademark, attribution notices, disclaimers of warranty, or limitations
+of liability ("notices") contained within the Program from any copy of
+the Program which they Distribute, provided that Contributors may add
+their own appropriate notices.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program,
+the Contributor who includes the Program in a commercial product
+offering should do so in a manner which does not create potential
+liability for other Contributors. Therefore, if a Contributor includes
+the Program in a commercial product offering, such Contributor
+("Commercial Contributor") hereby agrees to defend and indemnify every
+other Contributor ("Indemnified Contributor") against any losses,
+damages and costs (collectively "Losses") arising from claims, lawsuits
+and other legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the Program
+in a commercial product offering. The obligations in this section do not
+apply to any claims or Losses relating to any actual or alleged
+intellectual property infringement. In order to qualify, an Indemnified
+Contributor must: a) promptly notify the Commercial Contributor in
+writing of such claim, and b) allow the Commercial Contributor to control,
+and cooperate with the Commercial Contributor in, the defense and any
+related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those performance
+claims and warranties, and if a court requires any other Contributor to
+pay any damages as a result, the Commercial Contributor must pay
+those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
+BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
+TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+PURPOSE. Each Recipient is solely responsible for determining the
+appropriateness of using and distributing the Program and assumes all
+risks associated with its exercise of rights under this Agreement,
+including but not limited to the risks and costs of program errors,
+compliance with applicable laws, damage to or loss of data, programs
+or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other software
+or hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and
+may only be modified in the following manner. The Agreement Steward
+reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement
+Steward has the right to modify this Agreement. The Eclipse Foundation
+is the initial Agreement Steward. The Eclipse Foundation may assign the
+responsibility to serve as the Agreement Steward to a suitable separate
+entity. Each new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+Distributed subject to the version of the Agreement under which it was
+received. In addition, after a new version of the Agreement is published,
+Contributor may elect to Distribute the Program (including its
+Contributions) under the new version.
+
+Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+receives no rights or licenses to the intellectual property of any
+Contributor under this Agreement, whether expressly, by implication,
+estoppel or otherwise. All rights in the Program not expressly granted
+under this Agreement are reserved. Nothing in this Agreement is intended
+to be enforceable by any entity that is not a Contributor or Recipient.
+No third-party beneficiary rights are created under this Agreement.
+
+Exhibit A - Form of Secondary Licenses Notice
+
+"This Source Code may also be made available under the following
+Secondary Licenses when the conditions for such availability set forth
+in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+version(s), and exceptions or additional permissions here}."
+
+ Simply including a copy of this Agreement, including this Exhibit A
+ is not sufficient to license the Source Code under Secondary Licenses.
+
+ If it is not possible or desirable to put the notice in a particular
+ file, then You may include the notice in a location (such as a LICENSE
+ file in a relevant directory) where a recipient would be likely to
+ look for such a notice.
+
+ You may add additional accurate notices of copyright ownership.
\ No newline at end of file
diff --git a/SCTP_CNL113840.tpd b/SCTP_CNL113840.tpd
new file mode 100644
index 0000000..0df43e7
--- /dev/null
+++ b/SCTP_CNL113840.tpd
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<TITAN_Project_File_Information version="1.0">
+ <ProjectName>SCTP_CNL113840</ProjectName>
+ <ReferencedProjects>
+ <ReferencedProject name="SCTP_CNL113830" projectLocationURI="../../ProtocolModules/SCTP_CNL113830/SCTP_CNL113830.tpd"/>
+ </ReferencedProjects>
+ <Files>
+ <FileResource projectRelativePath="SCTP_Engine_Core.ttcn" relativeURI="src/SCTP_Engine_Core.ttcn"/>
+ <FileResource projectRelativePath="SCTP_Engine_Definition.ttcn" relativeURI="src/SCTP_Engine_Definition.ttcn"/>
+ <FileResource projectRelativePath="SCTP_Engine_External_Functions.cc" relativeURI="src/SCTP_Engine_External_Functions.cc"/>
+ <FileResource projectRelativePath="SCTP_Engine_Functions.ttcn" relativeURI="src/SCTP_Engine_Functions.ttcn"/>
+ <FileResource projectRelativePath="SCTP_Engine_PortTypes.ttcn" relativeURI="src/SCTP_Engine_PortTypes.ttcn"/>
+ <FileResource projectRelativePath="SCTP_Engine_Templates.ttcn" relativeURI="src/SCTP_Engine_Templates.ttcn"/>
+ </Files>
+ <ActiveConfiguration>Default</ActiveConfiguration>
+ <Configurations>
+ <Configuration name="Default">
+ <ProjectProperties>
+ <MakefileSettings>
+ <generateInternalMakefile>true</generateInternalMakefile>
+ <GNUMake>true</GNUMake>
+ <incrementalDependencyRefresh>true</incrementalDependencyRefresh>
+ <targetExecutable>bin/SCTP_CNL113840</targetExecutable>
+ </MakefileSettings>
+ <LocalBuildSettings>
+ <workingDirectory>bin</workingDirectory>
+ </LocalBuildSettings>
+ </ProjectProperties>
+ </Configuration>
+ </Configurations>
+</TITAN_Project_File_Information>
\ No newline at end of file
diff --git a/doc/SCTP_CNL113840_1551.doc b/doc/SCTP_CNL113840_1551.doc
new file mode 100644
index 0000000..3c0acc8
--- /dev/null
+++ b/doc/SCTP_CNL113840_1551.doc
Binary files differ
diff --git a/doc/SCTP_CNL113840_PRI.doc b/doc/SCTP_CNL113840_PRI.doc
new file mode 100644
index 0000000..eab15cf
--- /dev/null
+++ b/doc/SCTP_CNL113840_PRI.doc
Binary files differ
diff --git a/src/SCTP_Engine_Core.ttcn b/src/SCTP_Engine_Core.ttcn
new file mode 100644
index 0000000..2483edc
--- /dev/null
+++ b/src/SCTP_Engine_Core.ttcn
@@ -0,0 +1,205 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2018 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v2.0
+// which accompanies this distribution, and is available at
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+///////////////////////////////////////////////////////////////////////////////
+//
+// File: SCTP_Engine_Core.ttcn
+// Description: Core functions of the SCTP Engine
+// Rev: <RnXnn>
+// Prodnr: CNL 113 840
+//
+module SCTP_Engine_Core{
+
+import from SCTP_Engine_Definition all;
+import from SCTP_Engine_PortTypes all
+import from SCTP_Engine_Functions all
+import from SCTP_Types all
+
+//***************************************************************************//
+//
+// The main function, it should be started
+//
+//***************************************************************************//
+function SCTP_Core_main() runs on SCTP_Engine_CT {
+ // Start the run timer
+ t_run_time.start(2147482.0) // ~24 days. Can be set higher, but in that case
+ // the TITAN will flood the log with stupid warning
+
+ // Activate the handler altsteps
+ alt{
+ [] SCTP_core_lower_handler()
+ [] SCTP_Core_timer_handler()
+ [] SCTP_Core_upper_handler()
+ }
+
+
+}
+
+
+
+//***************************************************************************//
+//
+// Upper port event handler functions
+//
+//***************************************************************************//
+
+altstep SCTP_Core_upper_handler() runs on SCTP_Engine_CT {
+ var SCTP_Listen_data vl_listen_data;
+ var SCTP_Connect_data vl_connect_data;
+ var SCTP_Engine_CT vl_sender;
+ var SCTP_MSG_data vl_send_msg_data ;
+ var SCTP_Shutdown_data vl_shutdown_data ;
+ var SCTP_Reconfiguration_req vl_reconfig;
+ var integer vl_assoc_id;
+
+ [] p_sctp_req_api.getcall(S_SCTP_Listen:{?}) -> param (vl_listen_data) sender vl_sender{
+ SCTP_api_handle_listen(vl_listen_data,vl_sender)
+ repeat;
+ }
+ [] p_sctp_req_api.getcall(S_SCTP_Connect:{?}) -> param (vl_connect_data) sender vl_sender{
+ SCTP_api_handle_connect(vl_connect_data,vl_sender)
+ repeat;
+ }
+ [] p_sctp_req_api.getcall(S_SCTP_Send_req:{?}) -> param (vl_send_msg_data) sender vl_sender{
+ SCTP_api_handle_send_msg(vl_send_msg_data,vl_sender)
+ repeat;
+ }
+
+ [] p_sctp_req_api.getcall(S_SCTP_Shutdown:{?}) -> param (vl_shutdown_data) sender vl_sender{
+ SCTP_api_handle_shutdown(vl_shutdown_data,vl_sender)
+ repeat;
+ }
+
+ [] p_sctp_req_api.getcall(S_SCTP_Shutdown_conf:{?}) -> param (vl_assoc_id) sender vl_sender{
+ SCTP_api_handle_shutdown_conf(vl_assoc_id,vl_sender)
+ repeat;
+ }
+
+ [] p_sctp_req_api.getcall(S_SCTP_reconfig:{?}) -> param (vl_reconfig) sender vl_sender{
+ SCTP_api_handle_reconfig(vl_reconfig,vl_sender)
+ repeat;
+ }
+
+}
+
+//***************************************************************************//
+//
+// Time event handler functions
+//
+//***************************************************************************//
+altstep SCTP_Core_timer_handler() runs on SCTP_Engine_CT {
+ [] t_next_event.timeout {
+ // Store the event data
+ var integer vl_assoc_id:=v_event_db.events[v_event_db.the_sceduled_event].assoc_id
+ var integer vl_timer_id:=v_event_db.events[v_event_db.the_sceduled_event].timer_id
+ // remove the event from the queue
+ SCTP_event_remove(v_event_db.the_sceduled_event)
+
+ // Call the timer handler
+ select(vl_timer_id){
+ case (c_timer_T1) {
+ SCTP_timeout_T1_init(vl_assoc_id)
+ }
+ case (c_timer_T1_cookie) {
+ SCTP_timeout_T1_cookie(vl_assoc_id)
+ }
+ case (c_timer_T3_rtx) {
+ SCTP_timeout_T3_rtx(vl_assoc_id)
+ }
+ case (c_timer_T2_shutdown) {
+ SCTP_timeout_T2_shutdown(vl_assoc_id)
+ }
+ case (c_timer_heartbeat) {
+ SCTP_timeout_heartbeat(vl_assoc_id)
+ }
+ case (c_timer_reconfig) {
+ SCTP_timeout_reconfig(vl_assoc_id)
+ }
+ case (c_timer_close) {
+ SCTP_timeout_close(vl_assoc_id)
+ }
+ }
+ repeat;
+ }
+}
+
+
+//***************************************************************************//
+//
+// Lower port event handler functions
+//
+//***************************************************************************//
+
+altstep SCTP_core_lower_handler() runs on SCTP_Engine_CT {
+ var SCTP_Engine_packet vl_recv_packet;
+ var SCTP_Packet vl_sctp_pdu;
+ var integer vl_decode_res;
+
+ [] p_packet_port.receive(SCTP_Engine_packet:?) -> value vl_recv_packet {
+ // Process the incoming sctp packet
+ vl_decode_res:=f_SCTP_dec(vl_recv_packet.sctp_packet,vl_sctp_pdu);
+ //log("received: ",vl_decode_res)
+ if(vl_decode_res==0){ // Decode OK
+ // Find the association
+ var integer vl_assoc_id:=SCTP_find_id_map(vl_recv_packet.transport_id,
+ // incoming packet, the destination port is the local port
+ vl_sctp_pdu.common_header.destination_port,
+ vl_sctp_pdu.common_header.source_port)
+
+ //log("assoc id ",vl_assoc_id)
+ if(vl_assoc_id != -1 ){ // we found the assoctiation
+ SCTP_message_incoming_handler(vl_assoc_id,vl_sctp_pdu);
+ repeat;
+ } else {
+ // if it is an INIT or COOKIE ECHO chunk, try to find the listening
+ // association
+ if((lengthof(vl_sctp_pdu.chunks)>0) and
+ (ischosen(vl_sctp_pdu.chunks[0].init)
+ or ischosen(vl_sctp_pdu.chunks[0].cookie_echo) )
+ ){
+ // Lets find the listening association
+ vl_assoc_id:=SCTP_find_id_map(vl_recv_packet.transport_id,
+ // incoming packet, the destination port is the local port
+ vl_sctp_pdu.common_header.destination_port,
+ // the remote port is 0 for listening assoc
+ 0)
+ if(vl_assoc_id == -1){
+ // The listening assoc_id is not found
+ // Look for assoc listening on all transport
+ vl_assoc_id:=SCTP_find_id_map(-1, // All transport
+ // incoming packet, the destination port is the local port
+ vl_sctp_pdu.common_header.destination_port,
+ // the remote port is 0 for listening assoc
+ 0)
+
+ }
+
+
+ if(vl_assoc_id != -1 ){ // we found the assoctiation
+ SCTP_message_incoming_init_cookie_handler(vl_assoc_id,
+ vl_recv_packet.transport_id, vl_sctp_pdu);
+ repeat;
+ }
+ }
+ }
+ // Not processed message, Out of the blue packet
+ // Send abort
+ SCTP_message_process_oob_packet(vl_recv_packet.transport_id, vl_sctp_pdu);
+ } else {
+ // What the hell we received?
+ log("Undecodable packet: ", vl_recv_packet);
+ // just drop it
+ }
+ repeat;
+ }
+
+ [] p_packet_port.receive(SCTP_Addr_change_notification:?) {
+ repeat;
+ }
+
+}
+
+}
diff --git a/src/SCTP_Engine_Definition.ttcn b/src/SCTP_Engine_Definition.ttcn
new file mode 100644
index 0000000..40c4479
--- /dev/null
+++ b/src/SCTP_Engine_Definition.ttcn
@@ -0,0 +1,414 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2018 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v2.0
+// which accompanies this distribution, and is available at
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+///////////////////////////////////////////////////////////////////////////////
+//
+// File: SCTP_Engine_Definition.ttcn
+// Description: Component definition of the SCTP Engine
+// Rev: <RnXnn>
+// Prodnr: CNL 113 840
+//
+module SCTP_Engine_Definition {
+
+import from SCTP_Engine_PortTypes all;
+import from SCTP_Types all;
+
+modulepar SCTP_Proto_params tsp_proto_params:={ rto_initial := 3.0,
+ rto_min := 1.0,
+ rto_max := 60.0,
+ max_burst := 4,
+ rto_alpha := 0.125,
+ rto_beta := 0.25,
+ valid_cookie_life :=60,
+ assoc_max_retrans := 10,
+ path_max_retrans := 5,
+ max_init_retrans := 8,
+ hb_interval := 30.0,
+ hb_max_burst :=1,
+
+ // MTU discovery parameters
+ pmntu_min := 900, // This value is used to calculate the data chunk size
+ // The maximum data chunk size is
+ // pmntu_min - SCTP_common_header_size
+ // This ensures that the data chunk can be delivered every cases
+ // Normal ethernet frame size - (vpn,DTLS,UDP,IPv4 or IPv6) - something more
+ pmtu_max := 8960, // For Jumbo frames
+
+ pmtu_initial := 1200 // WebRTC draft
+
+ }
+
+modulepar SCTP_Reconfig_config tsp_reconf_params:={
+ is_supported := true,
+ stream_reset_allowed:= true,
+ assoc_reset_allowed:= true,
+ stream_add_allowed:= true
+}
+
+modulepar boolean tsp_forward_tsn_supported := true
+
+type component SCTP_Engine_CT {
+// Test ports
+ port SCTP_Engine_API_request_PT p_sctp_req_api // the upper side, SCTP test port
+ port SCTP_Engine_API_indication_PT p_sctp_ind_api // the upper side, SCTP test port
+ port SCTP_Engine_packet_PT p_packet_port // The lower side test port
+
+// main database
+ var SCTP_Assoc_DB_t v_assoc_db := c_empty_SCTP_Assoc_DB // The association data base
+
+ var octetstring v_secret_key := '11223344556677889900'O // the key used to generate MAC of the state cookie
+
+
+// time & event queue handling
+ timer t_run_time // timer used to mesure the time, started once
+ // with a very-very long timeout
+ timer t_next_event // expires on the next scheduled event
+
+ var SCTP_event_db_t v_event_db:= c_empty_event_db // Event queue database
+ var SCTP_Assoc_data c_empty_SCTP_Assoc_data:={
+ state := SCTP_ASSOC_CLOSED,
+ transport_id:=-1,
+ local_port:=-1,
+ remote_port:=-1,
+ own_tag:=-1,
+ remote_tag:=-1,
+ cookie_validity:=-1,
+ remote_tsn:=-1,
+ own_tsn:=-1,
+ remote_os:=-1,
+ remote_mis:=-1,
+ remote_a_rwnd:=-1,
+ cookie_lifespan:=-1,
+ state_cookie := ''O,
+ rem_comp := null,
+ i_data_supported := false,
+ proto_params := tsp_proto_params,
+ retransmit_counter:=0,
+ retransmit_timer_event := -1,
+ rto:=tsp_proto_params.rto_initial,
+ srtt:=0.0,
+ rttvar:=0.0,
+ pmtu:=tsp_proto_params.pmtu_initial,
+ tx_msg_queues := {},
+ tx_stream_idx_map_id := -1,
+ tx_chunk_queue := c_empty_chunk_queue_db,
+ cwnd:=0,
+ flight_size:=0,
+ partially_acked_bytes:=0,
+ ssthresh := 0,
+ scheduled_queue := 0,
+ remote_last_sack_tsn := -1,
+ rx_chunk_queue := c_empty_chunk_queue_db,
+ idx_of_last_acked := -1,
+ rx_msg_queues := {},
+ rx_stream_idx_map_id := -1,
+ hb_event_id := -1,
+ hb_retrial_counter := 0,
+ data_chunk_max_size := tsp_proto_params.pmntu_min-12, // 12: SCTP common header size. See up
+ reconf_params:=tsp_reconf_params,
+ reconf_own_seq_num:=-1,
+ reconf_remote_seq_num:=-1,
+ reconfig_ongoing := false,
+ forward_tsn_supported:=tsp_forward_tsn_supported,
+ reconfig_event_id := -1,
+ ongoing_incoming_reconf_req_seq := -1,
+ ongoing_outgoing_reconf_req_seq := -1,
+ last_reconf_resp := omit,
+ last_reconf_req := omit,
+ deffered_reset := omit,
+ reconfig_retransmit_counter:=0,
+ close_timer := -1
+ }
+}
+
+// Type for association data base
+type record SCTP_Assoc_DB_t{
+ // the data base realized by the list of the association entries.
+ // Each entries are not removed from the list, but reused if needed.
+ // In order to track the free elements, the free entries forms a linked list
+ // holding the index of the next free entry.
+ // The index of first & last free entries are stored also.
+
+
+ integer used_entries, // How many entries are used.
+ integer first_free, // points to the first free
+ integer last_free, // points to the last free
+
+ SCTP_Assoc_entry_list associations // The list of the entries
+}
+// constant used to initialize the v_assoc_db
+const SCTP_Assoc_DB_t c_empty_SCTP_Assoc_DB:={
+ used_entries := 0,
+ first_free :=-1,
+ last_free :=-1,
+ associations :={}
+}
+
+type record of SCTP_Assoc_entry SCTP_Assoc_entry_list
+
+type record SCTP_Assoc_entry {
+ integer next_free, // points to the next free entry if not used
+ SCTP_Assoc_data assoc_data optional // holds the data of the association
+ // if the entry is not used, the data
+ // is ommited in order to free the
+ // memory
+}
+
+const SCTP_Assoc_entry c_empty_SCTP_Assoc_entry:={
+ next_free := -1,
+ assoc_data := omit
+}
+
+
+// The value of the enumeration is ordered, so a range can be specified
+type enumerated SCTP_Assoc_state { SCTP_ASSOC_LISTEN(0),
+ SCTP_ASSOC_COOKIE_WAIT(1),SCTP_ASSOC_COOKIE_ECHOED(2),
+ SCTP_ASSOC_ESTABLISHED(3),
+ SCTP_ASSOC_SHUTDOWN_PENDING(4), SCTP_ASSOC_SHUTDOWN_RECEIVED(5),
+ SCTP_ASSOC_SHUTDOWN_SENT(6),
+ SCTP_ASSOC_SHUTDOWN_ACK_SENT(7), SCTP_ASSOC_CLOSED(8),
+ SCTP_ASSOC_CLOSE_TIME_WAIT(9) }
+
+// holds the data belongs to the association
+type record SCTP_Assoc_data {
+ SCTP_Assoc_state state,
+
+ integer transport_id, // The transport id
+ integer local_port, // Local sctp port number
+ integer remote_port, // Remote sctp port number
+ integer own_tag, // Own verification tag, The incoming message should come with this
+ integer remote_tag, // Remotes verification tag, set in the outgoing message
+ integer cookie_validity, // 0 - Cookie ok, 1- Cokkie expired. See the return value of SCTP_check_state_cookie
+ integer remote_tsn, // remote initial tsn, last sent SACK tsn + 1
+ integer own_tsn, // own next tsn/ initial tsn
+ integer remote_os, // remote outgoing sream number
+ integer remote_mis, // remote's max incoming stream number
+ integer remote_a_rwnd, // remote advertised receive window
+ integer cookie_lifespan, // The lifespan of our state cookie
+
+ octetstring state_cookie, // the stored received state cookie, used for retransmission
+
+ SCTP_Engine_CT rem_comp, // The component id of sctp user component
+
+ boolean i_data_supported, // Support I-DATA chunk
+
+ SCTP_Proto_params proto_params, // SCTP protocol parameters of the association
+ integer retransmit_counter, // the messages was retransmitted that many time without answer
+ integer retransmit_timer_event, // The event id of the retransmitt timer
+ float rto, // RTO
+ float srtt, // SRTT
+ float rttvar, // RTTVAR see: RFC 4960 6.3. Management of Retransmission Timer
+
+ integer pmtu, // The Path MTU
+
+ // Message queues
+ SCTP_msg_queue_list_t tx_msg_queues, // The queues of the TX messages, every stream has an own queue
+ // the Nth queue is the ordered msg queue, that idx is stored in the map below
+ // the N+1 th is for the unordered
+ integer tx_stream_idx_map_id, // The ID of the stream -> queue idx map, tells which stream belongs to which queue
+
+ SCTP_Chunk_queue_db_t tx_chunk_queue, // the TX DATA chunk queue
+
+ // Congestion control data see the rfc 4690
+ integer cwnd, // congestion window
+ integer flight_size, // sent but not acked bytes
+ integer partially_acked_bytes, // partially acknowledged bytes
+
+ integer ssthresh, // Slow start treshold
+
+ integer scheduled_queue, // the message from that queue will be sent
+
+ integer remote_last_sack_tsn, // The latest TSN acked by the remote
+
+ SCTP_Chunk_queue_db_t rx_chunk_queue, // the RX DATA chunk queue, used for reassembly
+ // How the queue is used: When a data chunk is received, it will be inserted into the queue
+ // If there are missing data chunk, a dummy placeholder is inserted into the queue
+ // The chunk field of the queue element is omited in that case
+
+ integer idx_of_last_acked, // the index of the last acked chunk in the queue
+
+ SCTP_msg_queue_list_t rx_msg_queues, // The queues of the RX messages, every stream has an own queue
+ // the Nth queue is the ordered msg queue, that idx is stored in the map below
+ // the N+1 th is for the unordered
+
+ integer rx_stream_idx_map_id, // The ID of the stream -> rx queue idx map, tells which stream belongs to which queue
+
+
+ integer hb_event_id, // The event id of the scheduled heartbeat event
+ integer hb_retrial_counter, // The heatbeat retrial counter
+ integer data_chunk_max_size, // the maximum size of the data chunk
+
+ SCTP_Reconfig_config reconf_params, // reconfiguration extension RFC6525
+ integer reconf_own_seq_num, // our next seq number/ initial one
+ integer reconf_remote_seq_num , // our next expected
+ boolean reconfig_ongoing,
+
+ boolean forward_tsn_supported, // indicates the support of RFC3758
+
+ integer reconfig_event_id,
+ integer ongoing_incoming_reconf_req_seq, // store the initiated "incomig type request": Incoming SSN Reset, Add Incoming Streams
+ integer ongoing_outgoing_reconf_req_seq, // store the initiated "outgoing type request": Outgoing SSN Reset, Add Outgoing Streams, SSN/TSN Reset Request
+ SCTP_Re_Config_chunk last_reconf_resp optional, // stores the last reconf response chunk for retransmission
+ SCTP_Re_Config_chunk last_reconf_req optional, // stores the last reconf request chunk for retransmission
+ SCTP_Out_SSN_Reset_req_parameter deffered_reset optional, // stores the reconf request for "deferred reset processing"
+ integer reconfig_retransmit_counter,
+ integer close_timer
+}
+
+
+// Parameters for reconfiguration extension RFC6525
+/*type record SCTP_Reconf_data {
+ boolean is_supported,
+ boolean stream_reset_allowed,
+ boolean assoc_reset_allowed,
+ boolean stream_add_allowed
+}*/
+
+// The type holds the SCTP Protocol Parameters
+// See RFC4960 chapter 15
+type record SCTP_Proto_params{
+ float rto_initial,
+ float rto_min,
+ float rto_max,
+ integer max_burst,
+ float rto_alpha,
+ float rto_beta,
+ integer valid_cookie_life,
+ integer assoc_max_retrans,
+ integer path_max_retrans,
+ integer max_init_retrans,
+ float hb_interval,
+ integer hb_max_burst,
+ integer pmntu_min,
+ integer pmtu_max,
+
+ integer pmtu_initial
+}
+
+
+// Event handling database
+// All of the scheduled events are stored in the event DB
+// One event DB entry stores the <assoc_id, timer_id>
+// the t_next_event timer is sceduled for the earliest event
+// in the case of timeout, adding or deleting event, the timer is recalculated
+// The events are stored in the same way as the assoc DB
+// The order of the events are stored & maintained in an external ordered std::map
+type record SCTP_event_db_t{
+ integer used_entries, // How many entries are used.
+ integer first_free, // points to the first free
+ integer last_free, // points to the last free
+
+ integer the_sceduled_event, // That event is used to start the t_next_event timer
+
+ SCTP_event_entry_list events // The list of the events
+}
+
+const SCTP_event_db_t c_empty_event_db:={
+ used_entries := 0,
+ first_free :=-1,
+ last_free :=-1,
+ the_sceduled_event := -1,
+ events :={}
+}
+
+type record of SCTP_event_entry SCTP_event_entry_list
+
+type record SCTP_event_entry {
+ integer next_free, // points to the next free entry if not used
+ integer assoc_id, // the event is scheduled by this assoc
+ integer timer_id, // the id of the assoc timer
+ float timeout_time // The absulute time of the timeout
+ // calculated as 'time_since_start' + 'timeout_val'
+}
+
+const SCTP_event_entry c_empty_event_entry:={
+ next_free:=-1,
+ assoc_id:=-1,
+ timer_id:=-1,
+ timeout_time:=-1.0
+}
+
+// Data types for queues
+
+// Chunk queue
+// Simple linked list, with free queue
+type record SCTP_Chunk_queue_db_t{
+ integer used_entries, // How many entries are used.
+ integer first_free, // points to the first free
+ integer last_free, // points to the last free
+ integer first_element, // points to the head of the chunk queue
+ integer last_element, // points to the tail of the chunk queue
+
+ SCTP_Chunk_queue_element_list_t chunk_queue
+}
+
+const SCTP_Chunk_queue_db_t c_empty_chunk_queue_db:={
+ used_entries := 0,
+ first_free :=-1,
+ last_free :=-1,
+ first_element :=-1,
+ last_element := -1,
+ chunk_queue :={}
+}
+
+type record of SCTP_Chunk_queue_element_t SCTP_Chunk_queue_element_list_t
+
+type record SCTP_Chunk_queue_element_t {
+ integer next_element,
+ boolean mark_flag, // used to mark the chunk in the queue:
+ // tx chunk queue: mark for retransmit
+ // rx chunk queue: added to the rx stream queue
+ float lifetime, // Used on sender side, partial reliability feature
+ integer counter, // Used on sender side, partial reliability feature
+ boolean unmark_flag, // tx queues: mark for abandon
+ integer tsn,
+ SCTP_Chunk chunk optional
+}
+
+const SCTP_Chunk_queue_element_t c_empty_Chunk_queue_element:={
+ next_element := -1,
+ mark_flag := false,
+ lifetime := -1.0,
+ counter := -1,
+ unmark_flag := false,
+ tsn := -1,
+ chunk := omit
+}
+
+// TX message queue types
+// Every outgoing stream has its own queue
+
+// represents one msg queue
+type record SCTP_msg_queue_t {
+ integer next_ssn, // the next SSN for the stream
+ integer num_of_queued_octets, // The number of the octets of the first message already put into the TX queue
+ boolean flag,
+ SCTP_Chunk_queue_db_t messages // Each message is represented by a DATA chunk
+}
+
+const SCTP_msg_queue_t c_empty_msg_queue:={
+ next_ssn := 0,
+ num_of_queued_octets := 0,
+ flag:=false,
+ messages := c_empty_chunk_queue_db
+}
+
+type record of SCTP_msg_queue_t SCTP_msg_queue_list_t
+
+type record of integer integer_list
+
+// Timer constants
+const integer c_timer_T1 := 0;
+const integer c_timer_T1_cookie := 1;
+const integer c_timer_T3_rtx := 2;
+const integer c_timer_T2_shutdown := 3;
+const integer c_timer_heartbeat := 4;
+const integer c_timer_reconfig := 5;
+const integer c_timer_close := 6;
+
+const integer c_min_chunk_size := 24 // The minimum data chunk size we use
+
+}
diff --git a/src/SCTP_Engine_External_Functions.cc b/src/SCTP_Engine_External_Functions.cc
new file mode 100644
index 0000000..5bd0b82
--- /dev/null
+++ b/src/SCTP_Engine_External_Functions.cc
@@ -0,0 +1,315 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2018 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v2.0
+// which accompanies this distribution, and is available at
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+///////////////////////////////////////////////////////////////////////////////
+//
+// File: SCTP_Engine_ExternalFunctions.cc
+// Description: function definition of the SCTP Engine
+// Rev: <RnXnn>
+// Prodnr: CNL 113 840
+//
+#include "SCTP_Engine_Functions.hh"
+#include <map>
+#include <vector>
+#include <queue>
+#include <stdint.h>
+#include <openssl/md5.h>
+#include <openssl/hmac.h>
+#include <time.h>
+
+typedef std::map<uint64_t,int> id_map_t;
+typedef id_map_t::iterator id_map_it_t;
+
+typedef std::map<uint64_t,uint64_t> ii_map_t;
+typedef ii_map_t::iterator ii_map_it_t;
+
+// Map to store assoc-id <-> (transport_id,local_port,remote_port) mapping
+// The (transport_id,local_port,remote_port) concatenated into a
+// 64 unsigned int value to form the key
+static id_map_t id_map;
+
+
+
+// Event scheduler types and map
+// std::multimap type is used, because it stores the elements in a
+// specific order
+// We need to order the event by the scheduled time -> use the time as key
+typedef std::multimap<double,int> event_map_t;
+typedef event_map_t::iterator event_map_it_t;
+static event_map_t event_map;
+
+
+// We need int2int maps for every association
+// - outgoing stream-id -> queue index
+// - incoming stream-id -> queue index
+//
+// These maps are allocated at the assoc creation and freed at the assoc deletion
+//
+
+static std::vector<ii_map_t*> ii_map_vector;
+static std::queue<int> ii_empty_queue; // stores the index of free/reusable elements
+
+namespace SCTP__Engine__Functions {
+
+//***************************************************************************//
+//
+// int2int map handler functions
+//
+//***************************************************************************//
+INTEGER SCTP__int2int__new(){
+ int idx=-1;
+ if(ii_empty_queue.empty()){
+ // No free item, add one
+ idx=ii_map_vector.size();
+ ii_map_vector.push_back(NULL);
+
+ } else {
+ // get the index of the free item
+ idx=ii_empty_queue.front();
+ ii_empty_queue.pop();
+ }
+ ii_map_vector[idx]=new ii_map_t;
+
+ return idx;
+
+}
+
+void SCTP__int2int__delete(INTEGER const&pl_map_id){
+ int idx=(int)pl_map_id;
+ if( idx>=0 && idx<(int)ii_map_vector.size() && ii_map_vector[idx] != NULL){
+ // valid index
+ // delete the map
+ delete ii_map_vector[idx];
+ ii_map_vector[idx]=NULL;
+ // put the map index into the empty queue
+ ii_empty_queue.push(idx);
+ }
+}
+
+INTEGER SCTP__int2int__add(INTEGER const& pl_map_id, INTEGER const& pl_key, INTEGER const& pl_value){
+ int idx=(int)pl_map_id;
+ if( idx>=0 && idx<(int)ii_map_vector.size() && ii_map_vector[idx] != NULL){
+ // valid index
+
+ (*(ii_map_vector[idx]))[(uint64_t)pl_key.get_long_long_val()]=(uint64_t)pl_value.get_long_long_val();
+ return 0;
+ }
+ return -1;
+}
+
+INTEGER SCTP__int2int__get(INTEGER const& pl_map_id, INTEGER const& pl_key, INTEGER& pl_value){
+ int idx=(int)pl_map_id;
+ if( idx>=0 && idx<(int)ii_map_vector.size() && ii_map_vector[idx] != NULL){
+ // valid index
+ ii_map_it_t it=(ii_map_vector[idx])->find((uint64_t)pl_key.get_long_long_val());
+ if(it!=(ii_map_vector[idx])->end()){
+ // valid key
+ pl_value.set_long_long_val(it->second);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+//***************************************************************************//
+//
+// assoc-id <-> (transport_id,local_port,remote_port) map handler functions
+//
+//***************************************************************************//
+
+// Constructs the 64 bit key value
+uint64_t get_id(const int64_t tr_id, const uint64_t loc_port, const uint64_t rem_port){
+ uint64_t vl_tr_id=0;
+ // the tr_id can be -1 => all transport => translate to all 1's
+ if(tr_id>=0){
+ vl_tr_id = tr_id;
+ } else {
+ vl_tr_id = ~vl_tr_id;
+ }
+ return (vl_tr_id << 32 ) | ((loc_port & 0xff) << 16 ) | (rem_port & 0xff);
+}
+
+INTEGER SCTP__add__id__map(INTEGER const& vl_assoc_id,
+ INTEGER const& vl_transport_id,
+ INTEGER const& vl_local_port,
+ INTEGER const& vl_remote_port){
+
+ uint64_t key=get_id(vl_transport_id.get_long_long_val(),(int)vl_local_port,(int)vl_remote_port);
+
+ if(id_map.find(key) == id_map.end()){
+ // if the key is not found, we can insert it
+ id_map[key]=(int)vl_assoc_id;
+
+ return 0;
+ }
+ // the key is found, return error
+ return -1;
+}
+INTEGER SCTP__del__id__map(INTEGER const& vl_transport_id,
+ INTEGER const& vl_local_port,
+ INTEGER const& vl_remote_port){
+ uint64_t key=get_id(vl_transport_id.get_long_long_val(),(int)vl_local_port,(int)vl_remote_port);
+ id_map_it_t it=id_map.find(key);
+ if(it != id_map.end()){
+ // found the key, delete it.
+ id_map.erase(it);
+ return 0; // Success
+ }
+ // the key is not found, return error
+ return -1;
+
+}
+INTEGER SCTP__find__id__map(INTEGER const& vl_transport_id,
+ INTEGER const& vl_local_port,
+ INTEGER const& vl_remote_port){
+ uint64_t key=get_id(vl_transport_id.get_long_long_val(),(int)vl_local_port,(int)vl_remote_port);
+ id_map_it_t it=id_map.find(key);
+ if(it != id_map.end()){
+ // found the key, return the assoc_id.
+
+ return it->second; // Success
+ }
+ // the key is not found, return error
+ return -1;
+
+}
+
+//***************************************************************************//
+//
+// State cookie handler functions
+//
+//***************************************************************************//
+
+OCTETSTRING SCTP__gen__state__cookie(INTEGER const& pl_lifespan,
+ INTEGER const& pl_own_init_tag,
+ INTEGER const& pl_remote_init_tag,
+ INTEGER const& pl_tie_tag,
+ INTEGER const& pl_remote_tsn,
+ INTEGER const& pl_remote_os,
+ INTEGER const& pl_remote_mis,
+ INTEGER const& pl_remote_a_rwnd,
+ BOOLEAN const& pl_idata_flag,
+ BOOLEAN const& pl_reconf_flag,
+ BOOLEAN const& pl_forward_tsn_flag,
+ OCTETSTRING const& pl_key){
+
+ time_t tstamp_t=time(NULL);
+ int tstamp_i = (tstamp_t &0x7fffffff ); // to calculate the lifespan of teh cookie
+ // the lowest 31 bit is enough
+
+ int lifespan = (int)pl_lifespan & 0xffff;
+
+ int zero = 0;
+ int ones = ~0;
+
+ OCTETSTRING ret_val= int2oct(tstamp_i,4) + int2oct(lifespan,2) +
+ int2oct(pl_own_init_tag,4)+ int2oct(pl_remote_init_tag,4)
+ + int2oct(pl_tie_tag,4)
+ + int2oct(pl_remote_tsn,4)+ int2oct(pl_remote_os,2)
+ + int2oct(pl_remote_mis,2)+ int2oct(pl_remote_a_rwnd,4)+
+ bit2oct( (pl_idata_flag?BITSTRING(1,(const unsigned char*)&ones):BITSTRING(1,(const unsigned char*)&zero))
+ + (pl_reconf_flag?BITSTRING(1,(const unsigned char*)&ones):BITSTRING(1,(const unsigned char*)&zero))
+ + (pl_forward_tsn_flag?BITSTRING(1,(const unsigned char*)&ones):BITSTRING(1,(const unsigned char*)&zero))
+ + BITSTRING(5,(const unsigned char*)&zero) )
+ ;
+
+ unsigned int out_length;
+ unsigned char output[EVP_MAX_MD_SIZE];
+ HMAC(EVP_md5(), (const unsigned char*)pl_key, (size_t) pl_key.lengthof(),
+ (const unsigned char*)ret_val, (size_t) ret_val.lengthof(),
+ output, &out_length);
+
+ return ret_val + OCTETSTRING(16,output);
+
+}
+
+INTEGER SCTP__check__state__cookie(OCTETSTRING const& pl_cookie,
+ OCTETSTRING const& pl_key){
+
+ if(pl_cookie.lengthof()!= 47){
+ // We use 46 octet length state cookie
+ return -1;
+ }
+
+ // check the MAC
+ unsigned int out_length;
+ unsigned char output[EVP_MAX_MD_SIZE];
+ HMAC(EVP_md5(), (const unsigned char*)pl_key, (size_t) pl_key.lengthof(),
+ (const unsigned char*)pl_cookie, 31,
+ output, &out_length);
+
+ if(substr(pl_cookie,31,16)!=OCTETSTRING(16,output)){
+ // MAC error
+ return -1;
+ }
+
+ // check lifespan
+ time_t tstamp_t=time(NULL);
+ unsigned int tstamp_i = (tstamp_t &0x7fffffff ); // to calculate the lifespan of teh cookie
+ // the lowest 31 bit is enough
+ unsigned int lifespan = (int)oct2int(substr(pl_cookie,4,2));
+ unsigned int orig_ts= (int)oct2int(substr(pl_cookie,0,4));
+
+ if( orig_ts<(tstamp_i-lifespan) ){
+ // expired
+ return 1;
+ }
+ // cookie ok
+ return 0;
+}
+
+//***************************************************************************//
+//
+// Event map handler functions
+//
+//***************************************************************************//
+void SCTP__event__add__map(INTEGER const& pl_event_id,
+ FLOAT const& pl_event_time){
+ // Just insert it
+ event_map.insert(std::pair<double,int>((double)pl_event_time,(int)pl_event_id));
+}
+
+void SCTP__event__del__map(INTEGER const& pl_event_id,
+ FLOAT const& pl_event_time){
+ // find the the time,id pair.
+ // Note that several event can be scheduled at the same time
+ // so it is not enough to delete the key.
+
+ event_map_it_t it=event_map.find((double)pl_event_time);
+ while( (it != event_map.end()) && (it->second != (int)pl_event_id ) ){
+ it++;
+ }
+
+ // now the it point to either the event ot delete or to the end
+ if(it != event_map.end()){
+ // delete the event
+ event_map.erase(it);
+ }
+
+}
+
+INTEGER SCTP__event__get__map() {
+ // return the event id of the first event.
+ // the map is ordered by the event time
+ event_map_it_t it=event_map.begin();
+ if(it != event_map.end()){
+ return it->second; // return the event id
+ }
+ return -1; // map is empty
+
+}
+
+OCTETSTRING SCTP__misc__float2oct(FLOAT const& pl_in){
+ double fl=(double)pl_in;
+ return OCTETSTRING(sizeof(double),(const unsigned char*)&fl);
+
+}
+FLOAT SCTP__misc__oct2float( OCTETSTRING const& pl_in){
+ return FLOAT(*((float*)(const unsigned char*)pl_in));
+}
+
+}
+
diff --git a/src/SCTP_Engine_Functions.ttcn b/src/SCTP_Engine_Functions.ttcn
new file mode 100644
index 0000000..27e39ce
--- /dev/null
+++ b/src/SCTP_Engine_Functions.ttcn
@@ -0,0 +1,4702 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2018 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v2.0
+// which accompanies this distribution, and is available at
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+///////////////////////////////////////////////////////////////////////////////
+//
+// File: SCTP_Engine_Functions.ttcn
+// Description: Functions of the SCTP Engine
+// Rev: <RnXnn>
+// Prodnr: CNL 113 840
+//
+module SCTP_Engine_Functions{
+
+import from SCTP_Engine_Definition all;
+import from SCTP_Engine_PortTypes all;
+import from SCTP_Types all
+import from SCTP_Engine_Templates all
+
+// Allocates an entry in the assoc DB, returns the index of the entry
+
+function SCTP_allocate_assoc_entry() runs on SCTP_Engine_CT return integer {
+ var integer vl_selected_entry:=-1;
+
+ if(v_assoc_db.used_entries<sizeof(v_assoc_db.associations)){
+ // There are free entries in the list
+ // remove it from the free list
+ vl_selected_entry:=v_assoc_db.first_free
+ v_assoc_db.first_free:=v_assoc_db.associations[vl_selected_entry].next_free
+ if(v_assoc_db.first_free == -1) {
+ // the last free entry was selected
+ v_assoc_db.last_free :=-1
+ }
+ v_assoc_db.associations[vl_selected_entry].next_free:=-1
+ } else {
+ // No free entry, add one to the list
+ vl_selected_entry:=sizeof(v_assoc_db.associations)
+ v_assoc_db.associations[vl_selected_entry] := c_empty_SCTP_Assoc_entry
+ v_assoc_db.used_entries:=v_assoc_db.used_entries+1
+ }
+
+ // initialize the data
+ v_assoc_db.associations[vl_selected_entry].assoc_data:=c_empty_SCTP_Assoc_data
+
+ // allocate int2int maps
+
+ v_assoc_db.associations[vl_selected_entry].assoc_data.tx_stream_idx_map_id := SCTP_int2int_new()
+ v_assoc_db.associations[vl_selected_entry].assoc_data.rx_stream_idx_map_id := SCTP_int2int_new()
+ return vl_selected_entry
+
+}
+
+// Put the association into closed state
+function SCTP_assoc_set_closed(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ // Stop all timers
+ SCTP_timer_stop_all(pl_assoc_id)
+
+ // Remove the association from the map
+ if( SCTP_del_id_map(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id ,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.local_port ,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port ) == -1) {
+ // Something went wrong
+ log("INTERNAL ERROR: SCTP_timeout_T1_init SCTP_del_id_map: the association is not found in the id map ",pl_assoc_id , " " , v_assoc_db.associations[pl_assoc_id])
+ }
+ // Set the state of the association
+ v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_CLOSED
+}
+
+// Constructs the assoc entry from Cookie echo message
+//
+// Return value:
+// - -1 error
+// - assoc_id
+function SCTP_assoc_construct_from_cookie_echo(in SCTP_Packet pl_sctp_packet,
+ in integer pl_transport_id := -1,
+ in integer pl_parent_id := -1
+ ) runs on SCTP_Engine_CT return integer{
+// first check the cookie validity
+ var integer pl_check_res :=-1
+ if( ( lengthof(pl_sctp_packet.chunks) >0 ) and ischosen(pl_sctp_packet.chunks[0].cookie_echo)){
+ // Seems to be a cookie echo message
+ // Check the cookie
+ pl_check_res:=SCTP_check_state_cookie(pl_sctp_packet.chunks[0].cookie_echo.cookie,v_secret_key);
+ if(pl_check_res == -1 ){
+ // invalid MAC
+ return -1;
+ }
+ // The cookie is either valid, or expired.
+ // Construct the assoc db entry. The caller will check for expired state
+ } else {
+ // Not a valid cookie echo
+ return -1;
+ }
+
+ // Allocate the new assoc data entry
+ var integer vl_new_assoc:=SCTP_allocate_assoc_entry();
+
+ // Copy the data from the parent if exists
+ if( pl_parent_id != -1) {
+ v_assoc_db.associations[vl_new_assoc]:=v_assoc_db.associations[pl_parent_id]
+ }
+
+ v_assoc_db.associations[vl_new_assoc].assoc_data:={
+ transport_id:=pl_transport_id,
+ local_port:=pl_sctp_packet.common_header.destination_port,
+ remote_port:=pl_sctp_packet.common_header.source_port,
+ cookie_validity:=pl_check_res
+ }
+
+ // extract data from state cookie
+ SCTP_cookie_extract(vl_new_assoc,pl_sctp_packet.chunks[0].cookie_echo.cookie);
+
+ return vl_new_assoc;
+}
+
+// Deallocates an entry in the assoc DB.
+function SCTP_free_assoc_entry(in integer pl_idx) runs on SCTP_Engine_CT {
+ if( (pl_idx<0) or (pl_idx>sizeof(v_assoc_db.associations))
+ or (not ispresent(v_assoc_db.associations[pl_idx].assoc_data)) ){
+ return // invalid index or item is already free, nothing to do
+ }
+
+ // delete the int2int maps
+ SCTP_int2int_delete(v_assoc_db.associations[pl_idx].assoc_data.tx_stream_idx_map_id)
+ SCTP_int2int_delete(v_assoc_db.associations[pl_idx].assoc_data.rx_stream_idx_map_id)
+
+ // Stop all timers
+ SCTP_timer_stop_all(pl_idx)
+
+ // release all the data
+ v_assoc_db.associations[pl_idx].assoc_data:=omit
+
+ // This entry will be the last free entry
+ v_assoc_db.associations[pl_idx].next_free := -1
+
+ // put the entry to the end of the chain
+ if(v_assoc_db.last_free!=-1){
+ // There was valid last free entry
+ v_assoc_db.associations[v_assoc_db.last_free].next_free:=pl_idx
+ }
+
+ // Move the last_free pointer
+ v_assoc_db.last_free:=pl_idx
+
+ v_assoc_db.used_entries:=v_assoc_db.used_entries-1
+
+ return // We're done
+}
+
+function SCTP_timer_stop_all(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+ }
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id:=-1
+ }
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer:=-1
+ }
+}
+
+// Function to handle assoc-id <-> (transport_id,local_port,remote_port) mapping
+// Register entries into the map
+// return value: 0 - OK, -1 - Error
+external function SCTP_add_id_map(in integer pl_assoc_id,
+ in integer pl_transport_id,
+ in integer pl_local_port,
+ in integer pl_remote_port) return integer
+
+// Delete entries from the map
+// return value: 0 - OK, -1 - Error
+external function SCTP_del_id_map(in integer pl_transport_id,
+ in integer pl_local_port,
+ in integer pl_remote_port) return integer
+
+// Search in the map
+// return value: assoc_id - OK, -1 - Error
+external function SCTP_find_id_map(in integer pl_transport_id,
+ in integer pl_local_port,
+ in integer pl_remote_port) return integer
+
+
+// State cookie handling functions
+// The information should be stored in state cookie
+// timestamp & lifespan
+// Own & remote verification tag
+// Own & remote tie tag
+// remote initial TSN, our own is equal to verification tag
+// output & max input stream of remote
+// remote advertised receive window
+// Those are needed to restore the association data
+// Our state cookie structure:
+
+// The order and size of the fields:
+// unix_timestamp 4 octets
+// pl_lifespan 2
+// pl_own_init_tag 4
+// pl_remote_init_tag 4
+// pl_tie_tag 4
+// pl_remote_tsn 4
+// pl_remote_os 2
+// pl_remote_mis 2
+// pl_remote_a_rwnd 4
+// flags 1
+// MAC 17
+// Summ:47
+// generate the state cookie. The actual time is queried.
+external function SCTP_gen_state_cookie(in integer pl_lifespan,
+ in integer pl_own_init_tag,
+ in integer pl_remote_init_tag,
+ in integer pl_tie_tag,
+ in integer pl_remote_tsn,
+ in integer pl_remote_os,
+ in integer pl_remote_mis,
+ in integer pl_remote_a_rwnd,
+ in boolean pl_idata_flag,
+ in boolean pl_reconf_flag,
+ in boolean pl_forward_tsn_flag,
+ in octetstring pl_key
+ ) return octetstring
+
+// Check the received state cookie validity
+// return values:
+// 0: OK
+// -1: MAC failed/ invalid contents
+// 1: expired
+external function SCTP_check_state_cookie(in octetstring pl_cookie,
+ in octetstring pl_key) return integer
+
+
+// extract the data from the cookie into the assoc db entry
+function SCTP_cookie_extract(in integer pl_assoc_id,
+ in octetstring pl_cookie) runs on SCTP_Engine_CT {
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data:={
+ own_tag:=oct2int(substr(pl_cookie,6,4)),
+ remote_tag:=oct2int(substr(pl_cookie,10,4)),
+ remote_tsn:=oct2int(substr(pl_cookie,18,4)),
+ own_tsn:=oct2int(substr(pl_cookie,6,4)),
+ remote_os:=oct2int(substr(pl_cookie,22,2)),
+ remote_mis := oct2int(substr(pl_cookie,24,2)),
+ remote_a_rwnd := oct2int(substr(pl_cookie,26,4)),
+ i_data_supported := substr(oct2bit(substr(pl_cookie,30,1)),0,1) == '1'B,
+ reconf_own_seq_num := oct2int(substr(pl_cookie,6,4)),
+ reconf_remote_seq_num := oct2int(substr(pl_cookie,18,4))
+ }
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported:= substr(oct2bit(substr(pl_cookie,30,1)),1,1) == '1'B
+ v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported:= substr(oct2bit(substr(pl_cookie,30,1)),2,1) == '1'B
+
+}
+/*****************************************************************************/
+//
+// Event scheduler functions
+//
+/*****************************************************************************/
+
+// Add event to the event-time map
+// return value: 0 - OK, -1 - Error
+external function SCTP_event_add_map(in integer pl_event_id,
+ in float pl_event_time)
+
+
+// Remove event from the map
+// return value: 0 - OK, -1 - Error
+external function SCTP_event_del_map(in integer pl_event_id,
+ in float pl_event_time)
+
+// Get the first event idx
+// return value: event_idx - OK, -1 - Error
+external function SCTP_event_get_map() return integer
+
+// Schedule the event
+// returns the idx of the
+function SCTP_event_schedule(in integer pl_assoc_id,
+ in integer pl_timer_id,
+ in float pl_timeout // relative timeout value
+ ) runs on SCTP_Engine_CT return integer {
+
+ var float curr_time:=t_run_time.read
+ var integer vl_selected_entry:=-1;
+// log(v_event_db)
+ if(v_event_db.used_entries<sizeof(v_event_db.events)){
+ // There are free entries in the list
+ // remove it from the free list
+ vl_selected_entry:=v_event_db.first_free
+ v_event_db.first_free:=v_event_db.events[vl_selected_entry].next_free
+ if(v_event_db.first_free == -1) {
+ // the last free entry was selected
+ v_event_db.last_free :=-1
+ }
+ v_event_db.events[vl_selected_entry].next_free:=-1
+ } else {
+ // No free entry, add one to the list
+ vl_selected_entry:=sizeof(v_event_db.events)
+ }
+ v_event_db.used_entries:=v_event_db.used_entries+1;
+
+ // initialize the data
+ v_event_db.events[vl_selected_entry] := {
+ next_free:=-1,
+ assoc_id:=pl_assoc_id,
+ timer_id:=pl_timer_id,
+ timeout_time:=curr_time+pl_timeout // absulute timeout val
+ }
+
+ // insert the event into the map
+ SCTP_event_add_map(vl_selected_entry,v_event_db.events[vl_selected_entry].timeout_time)
+
+ // rescedule the timeout timer
+ SCTP_event_recalculate_timer()
+
+ return vl_selected_entry
+}
+
+// Remove the event from the list
+function SCTP_event_remove(in integer pl_idx) runs on SCTP_Engine_CT {
+ if( (pl_idx<0) or (pl_idx>sizeof(v_event_db.events))
+ or (v_event_db.events[pl_idx].assoc_id==-1) ){
+ return // invalid index or item is already free, nothing to do
+ }
+
+ // remove the event from the map & reschedule the timer
+ SCTP_event_del_map(pl_idx,v_event_db.events[pl_idx].timeout_time)
+ SCTP_event_recalculate_timer()
+
+ // Free the enry and move to the end of the free chain
+ v_event_db.events[pl_idx]:=c_empty_event_entry
+
+ // put the entry to the end of the chain
+ if(v_event_db.last_free!=-1){
+ // There was valid last free entry
+ v_event_db.events[v_event_db.last_free].next_free:=pl_idx
+ }
+
+ // Set the first free pointer if needed
+ if(v_event_db.first_free==-1){
+ // There was valid last free entry
+ v_event_db.first_free:=pl_idx
+ }
+ // Move the last_free pointer
+ v_event_db.last_free:=pl_idx
+
+ v_event_db.used_entries:=v_event_db.used_entries-1;
+
+}
+
+// Set the t_next_event to the next event. Reset the timer if needed
+function SCTP_event_recalculate_timer() runs on SCTP_Engine_CT {
+ var integer first_event:=SCTP_event_get_map();
+
+ if(first_event==-1){
+ // No event in the queue
+ v_event_db.the_sceduled_event:=-1; // No scheduled event
+ if(t_next_event.running) {
+ t_next_event.stop // deactivate the timer
+ }
+ return // we're ready
+ }
+
+ if(first_event==v_event_db.the_sceduled_event){
+ // the first event is the already scheduled, nothing to do
+ return
+ }
+
+ // Reschedule the timer
+ if(t_next_event.running) {
+ t_next_event.stop // stop it first
+ }
+
+ var float curr_time:=t_run_time.read
+
+ // Calculate the timeout value
+ var float schedule_time:=v_event_db.events[first_event].timeout_time-curr_time
+ if(schedule_time<0.0){
+ // do not chedule in the past
+ schedule_time:=0.0
+ }
+
+ // remeber the event idx
+ v_event_db.the_sceduled_event:=first_event
+
+ // Start the timer
+ t_next_event.start(schedule_time)
+
+ return
+
+}
+
+/*****************************************************************************/
+//
+// Message handler functions
+//
+/*****************************************************************************/
+
+// Process the incoming sctp packet
+// The INIT & COOKIE ECHO processed by other function
+function SCTP_message_incoming_handler(in integer pl_assoc_id,
+ in SCTP_Packet pl_sctp_packet,
+ in boolean pl_cookie_preprocessed := false // if true, the Cookie Echo was preprocessed, only Cookie Ack is needed
+ ) runs on SCTP_Engine_CT {
+ // Check the verification tag
+ if(pl_sctp_packet.common_header.verification_tag!=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tag){
+ // Not for me
+ // Drop it
+ log("Verification tag mismatch. Packet dropped. ",pl_sctp_packet)
+ return;
+ }
+
+ // trigger the processing of the data queue
+ // If new chunk has been put into the incoming chunk queue, set it true to trigger the processing of the queue
+ var boolean vl_process_data:=false
+
+ // The answer packet
+ var SCTP_Packet vl_response := c_empty_sctp_packet
+
+ for(var integer vl_k:=0;vl_k<lengthof(pl_sctp_packet.chunks);vl_k:=vl_k+1){
+ // Process the chunks by calling the handler functions
+ // Each handler returns the processing result
+ // -1: Stop the further processing
+ // 0: Continue with the next chunk
+ // 1: Continue with the next chunk, postprocess the data queues
+ var integer vl_handler_ret:=-1
+ if(ischosen(pl_sctp_packet.chunks[vl_k].init_ack)){
+ vl_handler_ret:=SCTP_message_incoming_init_ack_handler(pl_sctp_packet.chunks[vl_k].init_ack,pl_assoc_id,vl_k)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].cookie_ack)){
+ vl_handler_ret:=SCTP_message_incoming_cookie_ack_handler(pl_sctp_packet.chunks[vl_k].cookie_ack,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].cookie_echo)){ //Process cookie echo
+ vl_handler_ret:=SCTP_message_incoming_cookie_echo_handler(pl_sctp_packet.chunks[vl_k].cookie_echo,pl_assoc_id,vl_response,pl_cookie_preprocessed)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].data) or ischosen(pl_sctp_packet.chunks[vl_k].idata)){ // process DATA
+ vl_handler_ret:=SCTP_message_incoming_data_handler(pl_sctp_packet.chunks[vl_k],pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].sack)){ // process SACK
+ vl_handler_ret:=SCTP_message_incoming_sack_handler(pl_sctp_packet.chunks[vl_k].sack,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].abort)){ // process ABORT
+ vl_handler_ret:=SCTP_message_incoming_abort_handler(pl_sctp_packet.chunks[vl_k].abort,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown)){ // process SHUTDOWN
+ vl_handler_ret:=SCTP_message_incoming_shutdown_handler(pl_sctp_packet.chunks[vl_k].shutdown,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown_ack)){ // process SHUTDOWN ACK
+ vl_handler_ret:=SCTP_message_incoming_shutdown_ack_handler(pl_sctp_packet.chunks[vl_k].shutdown_ack,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown_complete)){ // process SHUTDOWN COMPLETE
+ vl_handler_ret:=SCTP_message_incoming_shutdown_complete_handler(pl_sctp_packet.chunks[vl_k].shutdown_complete,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].heartbeat)){ // process HEARTBEAT
+ vl_handler_ret:=SCTP_message_incoming_heartbeat_handler(pl_sctp_packet.chunks[vl_k].heartbeat,pl_assoc_id,vl_response)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].heartbeat_ack)){ // process HEARTBEAT
+ vl_handler_ret:=SCTP_message_incoming_heartbeat_ack_handler(pl_sctp_packet.chunks[vl_k].heartbeat_ack,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].forward_tsn)){ // process FORWARD TSN
+ vl_handler_ret:=SCTP_message_incoming_forward_tsn_handler(pl_sctp_packet.chunks[vl_k].forward_tsn,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].iforward_tsn)){ // process FORWARD TSN
+ vl_handler_ret:=SCTP_message_incoming_iforward_tsn_handler(pl_sctp_packet.chunks[vl_k].iforward_tsn,pl_assoc_id)
+ } else if(ischosen(pl_sctp_packet.chunks[vl_k].re_config)){ // process FORWARD TSN
+ vl_handler_ret:=SCTP_message_incoming_re_config_handler(pl_sctp_packet.chunks[vl_k].re_config,vl_response,pl_assoc_id)
+ }
+
+
+ if(vl_handler_ret == -1 ){
+ return // -1: Stop the further processing
+ } else if(vl_handler_ret == 1 ){
+ vl_process_data:=true // 1: Continue with the next chunk, postprocess the data queues
+ }
+ } // for
+
+ if(vl_process_data){
+ // construct SACK
+ SCTP_data_gen_sack(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue, // The queue
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn, // The first expected tsn of the queue, if empty
+ vl_response.chunks[lengthof(vl_response.chunks)],
+ pl_assoc_id) // The chunk
+
+ // if "deferred reset processing" is ongoing and received the missing data chunks
+ if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset)
+ and (SCTP_compare_tsn(SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset.last_assigned_tsn),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0) ){
+ // then execute the reset
+ var integer vl_idx:=lengthof(vl_response.chunks)
+ vl_response.chunks[vl_idx]:={
+ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={}
+
+ }
+ }
+ SCTP_reconf_process_ssn_reset(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset,vl_response.chunks[vl_idx].re_config,pl_assoc_id)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset:=omit
+ }
+
+ // process the chunk queue and reconstruct the messages
+ SCTP_data_chunk2msg(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues,pl_assoc_id)
+
+ // deliver the data to the user
+ SCTP_data_deliver(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues,pl_assoc_id)
+
+ // Release the ack-ed and processed message from the rx chunk queue
+ SCTP_data_clean_tx_chunk_queue(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue,pl_assoc_id)
+
+
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.state == SCTP_ASSOC_SHUTDOWN_SENT){
+ // We should send shutdown also
+ var integer vl_sack_idx:=lengthof(vl_response.chunks)-1 // the index of the sack chunk
+ var SCTP_Shutdown_chunk vl_sh:={
+ chunk_type:=7,
+ flags:='00000000'B,
+ chunk_length:=0,
+ cum_tsn_ack:=vl_response.chunks[vl_sack_idx].sack.cum_tsn_ack
+ }
+ if(ispresent(vl_response.chunks[vl_sack_idx].sack.gap_blocks) or ispresent(vl_response.chunks[vl_sack_idx].sack.dup_tsns)){
+ // SACK is needed, add the shutdown after the sack
+ vl_response.chunks[lengthof(vl_response.chunks)].shutdown:=vl_sh
+ } else {
+ // SACK is not needed, replace it
+ vl_response.chunks[vl_sack_idx].shutdown:=vl_sh
+ }
+
+ // Restart the T2 timer
+ SCTP_timer_T2_restart(pl_assoc_id)
+
+ }
+ }
+
+ // Send the answer if need
+ if(lengthof(vl_response.chunks)>0){
+ vl_response.common_header:={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ }
+
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_response);
+ }
+}
+
+// The return values of the chunk handlers:
+// -1: Stop the further processing
+// 0: Continue with the next chunk
+// 1: Continue with the next chunk, postprocess the data queues
+
+
+// Handles the reconfig chunk
+function SCTP_message_incoming_re_config_handler(in SCTP_Re_Config_chunk pl_re_cfg, // The chunk to process
+ inout SCTP_Packet pl_response,
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+ // validate the chunk
+ var boolean vl_sendresp:=false
+ var boolean vl_storeas_req:=false
+
+ var SCTP_Re_Config_chunk vl_resp:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={}
+ }
+
+ if( v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported // re config support
+ and ispresent(pl_re_cfg.params)
+ and (
+ (lengthof(pl_re_cfg.params)==1) // one or two param present
+ or (lengthof(pl_re_cfg.params)==2) )
+ ){
+
+ // process the requests
+ for(var integer vl_i:=0;vl_i<lengthof(pl_re_cfg.params);vl_i:=vl_i+1){
+ if(ischosen(pl_re_cfg.params[vl_i].out_ssn_reset_req)){
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id!=-1){ // timer of outgoing request is running
+ if(pl_re_cfg.params[vl_i].out_ssn_reset_req.reconf_resp_seq == v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq){
+ // This is the acknowledgement of the sent incoming ssn reset request
+ // stop the timer
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=-1
+
+ // No need to notify the user here, because the notification will be sent below
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false // no outgoing reconfig request as it is acked
+ v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=omit // acked
+ v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=-1
+ }
+
+ }
+
+ // Check the seq no
+ var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].out_ssn_reset_req.reconf_req_seq
+ if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
+ or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
+ // this is just a hack to handle the case with two requests
+ and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
+ ){
+ // retransmission
+ // send the latest response again
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
+ return 1
+ }
+
+ if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
+ // not the expected seq no
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=5,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }}
+ }
+ }
+ return 0
+ }
+
+ // adjust the remote seq no
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
+
+ if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset)){
+ // reset already running
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=4,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }}
+ }
+ }
+ return 0
+
+ }
+
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_reset_allowed){ // Is SSN reset allowed?
+ if(SCTP_compare_tsn(SCTP_next_tsn(pl_re_cfg.params[vl_i].out_ssn_reset_req.last_assigned_tsn),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)>0){
+ // The remote last assigned TSN is greater than the sack-ed
+ // Enter "deferred reset processing"
+
+ // store the packet for the later processing
+ v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset:=pl_re_cfg.params[vl_i].out_ssn_reset_req
+
+ // prepare response
+ vl_resp.params[lengthof(vl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=6,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ vl_sendresp:=true
+
+ } else {
+ SCTP_reconf_process_ssn_reset(pl_re_cfg.params[vl_i].out_ssn_reset_req,vl_resp,pl_assoc_id)
+ vl_sendresp:=true
+ }
+ } else {
+ // send back the denied
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=2,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }}
+ }
+ }
+ return 0
+
+ }
+
+ } else if(ischosen(pl_re_cfg.params[vl_i].in_ssn_reset_req)){
+ // check seq no
+ // Check the seq no
+ var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].in_ssn_reset_req.reconf_req_seq
+ if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
+ or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
+ // this is just a hack to handle the case with two requests
+ and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
+ ){
+ // retransmission
+ // send the latest response again
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
+ return 1
+ }
+
+ if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
+ // not the expected seq no
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=5,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }}
+ }
+ }
+ return 0
+ }
+
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing){
+ // request is sent but not completed
+ // just ignore it
+ // The remote side will retransmit later
+
+ } else {
+ // TODO: handle request overlap
+ // generate outgoing ssn reset request
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_reset_allowed){ // Is SSN reset allowed?
+ if(ispresent(pl_re_cfg.params[vl_i].in_ssn_reset_req.stream_numbers)){
+ SCTP_reconf_generate_out_ssn_reset_req(pl_re_cfg.params[vl_i].in_ssn_reset_req.stream_numbers,vl_in_seq_no,vl_resp,pl_assoc_id)
+ } else {
+ SCTP_reconf_generate_out_ssn_reset_req({},vl_in_seq_no,vl_resp,pl_assoc_id)
+ }
+
+ vl_sendresp:=true;
+ }else {
+ // send back the denied
+ vl_resp.params[lengthof(vl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=2,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ vl_sendresp:=true;
+ }
+
+ }
+ } else if(ischosen(pl_re_cfg.params[vl_i].add_out_stream)){
+ var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].add_out_stream.reconf_req_seq
+ if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
+ or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
+ // this is just a hack to handle the case with two requests
+ and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
+ ){
+ // retransmission
+ // send the latest response again
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
+ return 1
+ }
+
+ if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
+ // not the expected seq no
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=5,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }}
+ }
+ }
+ return 0
+ }
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_add_allowed){ // allowed?
+ // send back ok
+ vl_resp.params[lengthof(vl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=1,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ vl_sendresp:=true;
+
+ // notify the user
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_OK,
+ method:={add_stream:={
+ incoming:=false,
+ new_streams:=pl_re_cfg.params[vl_i].add_out_stream.stream_num
+ }
+ }
+ }
+ },
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ } else {
+ // send back the denied
+ vl_resp.params[lengthof(vl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=2,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ vl_sendresp:=true;
+ }
+
+ } else if(ischosen(pl_re_cfg.params[vl_i].add_in_stream)){
+ var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].add_in_stream.reconf_req_seq
+ if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
+ or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
+ // this is just a hack to handle the case with two requests
+ and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
+ ){
+ // retransmission
+ // send the latest response again
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
+ return 1
+ }
+
+ if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
+ // not the expected seq no
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=5,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }}
+ }
+ }
+ return 0
+ }
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_add_allowed){ // Is allowed?
+ // send back ok
+ vl_resp.params[lengthof(vl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=1,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ vl_sendresp:=true;
+
+ // send the add outgoing stream req
+ var SCTP_Packet vl_out_req:=c_empty_sctp_packet
+ vl_out_req.common_header:={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ }
+ vl_out_req.chunks[0]:={ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{
+ add_out_stream:={
+ param_type:=17,
+ param_length:=0,
+ reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num,
+ stream_num:=pl_re_cfg.params[vl_i].add_in_stream.stream_num,
+ reserved:=0
+ }
+ }}
+ }
+
+ }
+ v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num:=SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=vl_out_req.chunks[0].re_config
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0
+
+ // schedule the timer
+ SCTP_timer_reconf_start(pl_assoc_id)
+
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_out_req);
+
+ } else {
+ // send back the denied
+ vl_resp.params[lengthof(vl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=2,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ vl_sendresp:=true;
+ }
+
+ } else if(ischosen(pl_re_cfg.params[vl_i].ssn_tsn_reset_req)){
+ var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].ssn_tsn_reset_req.reconf_req_seq
+ if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
+ or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
+ // this is just a hack to handle the case with two requests
+ and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
+ ){
+ // retransmission
+ // send the latest response again
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
+ return 1
+ }
+
+ if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
+ // not the expected seq no
+ pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=5,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }}
+ }
+ }
+ return 0
+ }
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
+
+ // process the request
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.assoc_reset_allowed){ // Is reset allowed?
+ // Compute an appropriate value for the Receiver's Next TSN
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:= (v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn + 2147483648) mod 4294967296
+
+ // Compute an appropriate value for the local endpoint's next TSN
+ // v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn already contains that value
+
+ // Reset the stream and chunk queues
+
+ // clear the incoming message queues
+ for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue
+ }
+
+ // Clear incoming chunk queue
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue := c_empty_chunk_queue_db
+
+ // Clear outgoing chunk queue
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue := c_empty_chunk_queue_db
+
+ // clear the outgoing message queues
+ for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues);vl_k:=vl_k+1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_k]:=c_empty_msg_queue
+ }
+
+ // stop the T3 timer.
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+ }
+
+ vl_resp.params[lengthof(vl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=1,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ vl_sendresp:=true;
+
+ // notify the user
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_OK,
+ method:={assoc_reset:={}
+ }
+ }
+ },
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+
+ } else {
+ // send back the denied
+ vl_resp.params[lengthof(vl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=vl_in_seq_no,
+ result:=2,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ vl_sendresp:=true;
+ }
+
+
+ } else if(ischosen(pl_re_cfg.params[vl_i].reconf_resp)){
+ if(pl_re_cfg.params[vl_i].reconf_resp.reconf_resp_seq==v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq){
+ // we got a response for our request
+ // stop the timers
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false
+ SCTP_timer_reconf_stop(pl_assoc_id)
+
+ // get the request type
+ // check the answere code
+ if(pl_re_cfg.params[vl_i].reconf_resp.result==2){
+ // denied
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_DENY,
+ method:=omit
+ }
+ },
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+ SCTP_reconf_clear_marks(pl_assoc_id)
+ } else if(pl_re_cfg.params[vl_i].reconf_resp.result==6) {
+ // In progress, restart the timer and clear the retransmission counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0
+ SCTP_timer_reconf_start(pl_assoc_id)
+ } else if(pl_re_cfg.params[vl_i].reconf_resp.result>2 and pl_re_cfg.params[vl_i].reconf_resp.result<6) {
+ // fail
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_FAIL,
+ method:=omit
+ }
+ },
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+ SCTP_reconf_clear_marks(pl_assoc_id)
+ } else {
+ // success
+ // check the type of the request
+ var SCTP_parameters vl_req
+ if(SCTP_reconf_get_out_req( v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req,vl_req)){
+ if(ischosen(vl_req.add_out_stream)){
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_OK,
+ method:= {add_stream:={
+ incoming:= true,
+ new_streams:=vl_req.add_out_stream.stream_num
+ }
+ }
+ }
+ },
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ } else if(ischosen(vl_req.out_ssn_reset_req)){
+ // reset the ssn's
+ // the queues are already marked
+ for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k].flag){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue
+ }
+ }
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_OK,
+ method:= {stream_reset:={
+ incoming:= true,
+ streams:=vl_req.out_ssn_reset_req.stream_numbers
+ }
+ }
+ }
+ },
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ } else if(ischosen(vl_req.ssn_tsn_reset_req)){
+ // reset the assoc
+ // clear the incoming message queues
+ for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue
+ }
+
+ // Clear incoming chunk queue
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue := c_empty_chunk_queue_db
+
+ // Clear outgoing chunk queue
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue := c_empty_chunk_queue_db
+
+ // clear the outgoing message queues
+ for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues);vl_k:=vl_k+1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_k]:=c_empty_msg_queue
+ }
+
+ // stop the T3 timer.
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+ }
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:= pl_re_cfg.params[vl_i].reconf_resp.sender_next_tsn
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=pl_re_cfg.params[vl_i].reconf_resp.receiver_next_tsn
+
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_OK,
+ method:= {assoc_reset:={
+ }
+ }
+ }},
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ }
+ }
+ }
+
+
+ } else if(pl_re_cfg.params[vl_i].reconf_resp.reconf_resp_seq==v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq){
+ // we got a response for our request of do something with the incoming streams
+ // Just stop the timer as we should receive matching request
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false
+ SCTP_timer_reconf_stop(pl_assoc_id)
+
+ // check the answere code
+ if(pl_re_cfg.params[vl_i].reconf_resp.result==2){
+ // denied
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_DENY,
+ method:=omit
+ }
+ },
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ } else if(pl_re_cfg.params[vl_i].reconf_resp.result>2) {
+ // fail
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_FAIL,
+ method:=omit
+ }
+ },
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+ }
+ }
+ // else do nothing
+ }
+
+
+
+ } // for
+
+
+ } else {
+ // protocol error
+ pl_response.chunks[lengthof(pl_response.chunks)]:={
+ error_:={
+ chunk_type := 9,
+ flags:= '00000000'B,
+ chunk_length:=0,
+ params:={
+ {protocol_violation:={cause_code:=13,cause_length:=0,reason:=''O}}
+ }
+ }
+ }
+ }
+
+ if(vl_sendresp){
+ pl_response.chunks[lengthof(pl_response.chunks)]:={re_config:=vl_resp}
+ v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp:=vl_resp
+
+ }
+
+ return 0
+}
+
+function SCTP_reconf_get_out_req(in SCTP_Re_Config_chunk pl_chunk, out SCTP_parameters pl_req) return boolean {
+
+ if(ispresent(pl_chunk.params)){
+ for(var integer vl_i; vl_i<lengthof(pl_chunk.params);vl_i:=vl_i+1){
+ if(ischosen(pl_chunk.params[vl_i].out_ssn_reset_req)
+ or ischosen(pl_chunk.params[vl_i].add_out_stream)
+ or ischosen(pl_chunk.params[vl_i].ssn_tsn_reset_req)){
+ pl_req:=pl_chunk.params[vl_i]
+ return true
+ }
+ }
+ }
+ return false
+}
+function SCTP_reconf_clear_marks(in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT{
+
+ for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i].flag:=false;
+ }
+}
+function SCTP_reconf_generate_out_ssn_reset_req( in integer_list pl_stream_list,
+ in integer pl_in_seq_no,
+ inout SCTP_Re_Config_chunk pl_resp,
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT{
+
+ var integer vl_idx:=lengthof(pl_resp.params)
+ pl_resp.params[vl_idx]:={out_ssn_reset_req:={
+ param_type:=13,
+ param_length:=0,
+ reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num,
+ reconf_resp_seq:=pl_in_seq_no,
+ last_assigned_tsn:=SCTP_prev_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn),
+ stream_numbers:=omit
+ }}
+
+ if(lengthof(pl_stream_list)>0){
+ pl_resp.params[vl_idx].out_ssn_reset_req.stream_numbers:=pl_stream_list
+ // mark the lists as closed
+ for(var integer vl_i:=0;vl_i<lengthof(pl_stream_list);vl_i:=vl_i+1){
+ var integer vl_stream_idx
+ if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
+ pl_stream_list[vl_i],
+ vl_stream_idx
+ )!=-1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx].flag:=true;
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx+1].flag:=true;
+ }
+
+ }
+
+ } else {
+ // mark all stream as closed
+ for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i].flag:=true;
+ }
+
+ }
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num:=SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=pl_resp
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0
+
+ // schedule the timer
+ SCTP_timer_reconf_start(pl_assoc_id)
+
+}
+
+function SCTP_reconf_process_ssn_reset(in SCTP_Out_SSN_Reset_req_parameter pl_re_cfg, // The chunk to process
+ inout SCTP_Re_Config_chunk pl_resp,
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT{
+ var SCTP_Notification_data vl_note_data:={
+ assoc_id:=pl_assoc_id,
+ notification := {
+ reconfig:={
+ result:=RECONFIG_OK,
+ method:={stream_reset:={
+ incoming:=false,
+ streams:={}
+ }
+ }
+ }
+ },
+ options := omit
+ }
+
+ if(ispresent(pl_re_cfg.stream_numbers)){
+ // reset only the specified streams
+ for(var integer vl_i:=0;vl_i<lengthof(pl_re_cfg.stream_numbers);vl_i:=vl_i+1){
+ var integer vl_stream_idx
+ vl_note_data.notification.reconfig.method.stream_reset.streams[lengthof(vl_note_data.notification.reconfig.method.stream_reset.streams)]:=pl_re_cfg.stream_numbers[vl_i]
+ if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
+ pl_re_cfg.stream_numbers[vl_i],
+ vl_stream_idx
+ )!=-1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx]:=c_empty_msg_queue
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx+1]:=c_empty_msg_queue
+ }
+ }
+ } else {
+ // reset all streams
+ for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i]:=c_empty_msg_queue
+ }
+ }
+
+ // generate the ack message
+ pl_resp.params[lengthof(pl_resp.params)]:={
+ reconf_resp:={
+ param_type:=16,
+ param_length:=0,
+ reconf_resp_seq:=pl_re_cfg.reconf_req_seq,
+ result:=1,
+ sender_next_tsn:=omit,
+ receiver_next_tsn:=omit
+ }
+ }
+ v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp:=pl_resp
+ // Notify the user
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+}
+
+// process I-forward TSN chunk
+function SCTP_message_incoming_iforward_tsn_handler(in SCTP_IForward_TSN_chunk pl_fwd_tsn, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+
+ if(SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<0){
+ // The new TSN has been acked already. Just trigger the SACK
+ return 1
+ }
+
+ // Clean up the data queue
+ // Mark every hole as processed packet until the new tsn
+ // The data processor part will do the magic later
+
+ var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.first_element
+
+ while(vl_idx!=-1){
+ if( SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].tsn)<0){
+ // the tsn of the chunk is greater than the new tsn
+ break; // we are done
+ }
+
+ if(not ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].chunk)){
+ // A hole, mark it.
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].mark_flag:= true
+ }
+ vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].next_element
+ }
+
+ // clean up the queues
+ for(var integer vl_i:=0;vl_i<lengthof(pl_fwd_tsn.stream_ssn_list);vl_i:=vl_i+1){
+ SCTP_message_queue_skip_ssn(pl_assoc_id,pl_fwd_tsn.stream_ssn_list[vl_i].stream_id,pl_fwd_tsn.stream_ssn_list[vl_i].mid mod 65536,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues )
+ }
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(pl_fwd_tsn.new_tsn)
+ return 1 // trigger the data processing
+}
+
+
+// process forward TSN chunk
+function SCTP_message_incoming_forward_tsn_handler(in SCTP_Forward_TSN_chunk pl_fwd_tsn, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+
+ if(SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<0){
+ // The new TSN has been acked already. Just trigger the SACK
+ return 1
+ }
+
+ // Clean up the data queue
+ // Mark every hole as processed packet until the new tsn
+ // The data processor part will do the magic later
+
+ var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.first_element
+
+ while(vl_idx!=-1){
+ if( SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].tsn)<0){
+ // the tsn of the chunk is greater than the new tsn
+ break; // we are done
+ }
+
+ if(not ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].chunk)){
+ // A hole, mark it.
+ v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].mark_flag:= true
+ }
+ vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].next_element
+ }
+
+ // clean up the queues
+ for(var integer vl_i:=0;vl_i<lengthof(pl_fwd_tsn.stream_ssn_list);vl_i:=vl_i+1){
+ SCTP_message_queue_skip_ssn(pl_assoc_id,pl_fwd_tsn.stream_ssn_list[vl_i].stream_id,pl_fwd_tsn.stream_ssn_list[vl_i].ssn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues )
+ }
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(pl_fwd_tsn.new_tsn)
+ return 1 // trigger the data processing
+}
+
+// Mark the messages as skiped based in the forward tsn chunk data
+function SCTP_message_queue_skip_ssn( in integer pl_assoc_id, // The association
+ in integer pl_stream_id,
+ in integer pl_skipped_ssn,
+ inout SCTP_msg_queue_list_t pl_msg_queue_list
+ ) runs on SCTP_Engine_CT {
+
+ var integer vl_stream_idx:=-1; // index of the stream in the pl_msg_queue_list
+ if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
+ pl_stream_id,
+ vl_stream_idx
+ )==-1){
+ // not seen stream
+ // create it
+ vl_stream_idx:=lengthof(pl_msg_queue_list)
+ // ordered queue
+ pl_msg_queue_list[vl_stream_idx]:=c_empty_msg_queue;
+ pl_msg_queue_list[vl_stream_idx].next_ssn:=SCTP_next_ssn(pl_skipped_ssn); // store the next expected ssn
+
+ // undered queue
+ pl_msg_queue_list[vl_stream_idx+1]:=c_empty_msg_queue;
+ pl_msg_queue_list[vl_stream_idx+1].next_ssn:=-1; // Not used queue
+
+ } else {
+ var integer vl_idx:=pl_msg_queue_list[vl_stream_idx].messages.first_element
+ while(vl_idx!=-1){
+ if(SCTP_compare_ssn(SCTP_data_get_chunk_ssn(pl_msg_queue_list[vl_stream_idx].messages.chunk_queue[vl_idx].chunk),pl_skipped_ssn )<=0){
+ // ssn less than the last skiped, mark it
+ pl_msg_queue_list[vl_stream_idx].messages.chunk_queue[vl_idx].mark_flag:=true
+ } else {
+ break; //no more ssn to mark
+ }
+ }
+
+ if(SCTP_compare_ssn(pl_msg_queue_list[vl_stream_idx].next_ssn,pl_skipped_ssn)<=0){
+ // afdjust the next ssn if needed
+ pl_msg_queue_list[vl_stream_idx].next_ssn:=SCTP_next_ssn(pl_skipped_ssn)
+ }
+
+ }
+
+}
+
+// Handles the incoming HEARTBEAT_ACK
+function SCTP_message_incoming_heartbeat_ack_handler(in SCTP_Heartbeat_ack_chunk pl_hb_ack, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+
+ // reset the hearbeat counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=0
+ return 0
+}
+// Handles the incoming HEARTBEAT
+function SCTP_message_incoming_heartbeat_handler(in SCTP_Heartbeat_chunk pl_hb, // The chunk to process
+ in integer pl_assoc_id, // The association
+ inout SCTP_Packet pl_response // Add the heartbeat_ack to the response
+ ) runs on SCTP_Engine_CT return integer {
+
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case ( (SCTP_ASSOC_COOKIE_ECHOED,SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_RECEIVED)){
+ // A receiver of a HEARTBEAT MUST respond to a
+ // HEARTBEAT with a HEARTBEAT-ACK after entering the COOKIE-ECHOED state
+ // (INIT sender) or the ESTABLISHED state (INIT receiver), up until
+ // reaching the SHUTDOWN-SENT state (SHUTDOWN sender) or the SHUTDOWN-
+ // ACK-SENT state (SHUTDOWN receiver).
+
+ // Add the heartbeat ack chunk to the response
+ pl_response.chunks[lengthof(pl_response.chunks)]:={heartbeat_ack:={ chunk_type :=5,
+ flags := '00000000'B,
+ chunk_length := 0,
+ params :=pl_hb.params
+ }}
+
+ }
+ }
+
+
+
+ return 0
+}
+
+// Handles the incoming SHUTDOWN COMPLETE
+function SCTP_message_incoming_shutdown_complete_handler(in SCTP_ShutdownComplete_chunk pl_shutdown, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case ( (SCTP_ASSOC_SHUTDOWN_SENT,SCTP_ASSOC_SHUTDOWN_ACK_SENT)){
+
+ // Notify the user
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := true,
+ reason := "Shutdown finished"
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(pl_assoc_id)
+
+ }
+ }
+ return -1
+
+}
+
+// Handles the incoming SHUTDOWN ACK
+function SCTP_message_incoming_shutdown_ack_handler(in SCTP_Shutdown_ack_chunk pl_shutdown, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case ( (SCTP_ASSOC_SHUTDOWN_SENT,SCTP_ASSOC_SHUTDOWN_ACK_SENT)){
+
+
+ SCTP_timer_stop_all(pl_assoc_id)
+ // Send Shutdown Complete
+ SCTP_message_send_shutdown_complete(-,-,pl_assoc_id)
+
+ // delay the shutdown ind in order to give a chance to deliver the shutdown complete message
+ // The user may destroy the underlying transport in the case of shutdown ind
+ v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_CLOSE_TIME_WAIT
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer:=SCTP_event_schedule(pl_assoc_id,c_timer_close,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
+
+
+ }
+ }
+ return -1
+}
+
+
+// Handles the incoming SHUTDOWN
+function SCTP_message_incoming_shutdown_handler(in SCTP_Shutdown_chunk pl_shutdown, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case ( (SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING)){
+ // Set the state
+ v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_SHUTDOWN_RECEIVED
+
+ // Process the cum_tsn_ack
+ SCTP_message_cumulative_ack_handler(pl_shutdown.cum_tsn_ack,pl_assoc_id)
+ // Schedule the sending of the data
+ // The data send scheduler will check the state and if no outstanding data
+ // will send shutdown ack and move to
+ SCTP_data_send_scheduler(pl_assoc_id)
+
+ // Notify the user
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.state==SCTP_ASSOC_ESTABLISHED){
+ var SCTP_Notification_data vl_notification:={
+ assoc_id := pl_assoc_id,
+ notification:= {comm_lost := {}},
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+ }
+
+ }
+ case (SCTP_ASSOC_SHUTDOWN_RECEIVED){
+ // Process the cum_tsn_ack
+ SCTP_message_cumulative_ack_handler(pl_shutdown.cum_tsn_ack,pl_assoc_id)
+ // Schedule the sending of the data
+ // The data send scheduler will check the state and if no outstanding data
+ // will send shutdown ack and move to
+ SCTP_data_send_scheduler(pl_assoc_id)
+
+ }
+ case (SCTP_ASSOC_SHUTDOWN_SENT){
+ // Set the state
+ v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_SHUTDOWN_RECEIVED
+ // The data send scheduler will check the state and if no outstanding data
+ // will send shutdown ack
+ SCTP_data_send_scheduler(pl_assoc_id)
+ }
+ }
+ return 0
+}
+
+// Handles the incoming ABORT
+function SCTP_message_incoming_abort_handler(in SCTP_abort_chunk pl_abort, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+
+ // Notify the user
+ var SCTP_Notification_data vl_notification:={
+ assoc_id := pl_assoc_id,
+ notification:= {comm_lost := {}},
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := false,
+ reason := "Abort received"
+ }
+
+ // Store the reason if available
+ if(ispresent(pl_abort.params)){
+ vl_sh_data.reason:=log2str(pl_abort.params)
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(pl_assoc_id)
+ // Stop the further processing
+ return -1
+}
+
+// Handles the incoming SACK
+function SCTP_message_incoming_sack_handler(in SCTP_SAck_chunk pl_sack, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case ((SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_RECEIVED)){
+ var integer vl_tsn_comp:=SCTP_message_cumulative_ack_handler( pl_sack.cum_tsn_ack,pl_assoc_id)
+ if(vl_tsn_comp==-1){
+ // TSN already acked
+ // do nothing
+ return 0 // next chunk
+ }
+ // update the peers window size
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd:= pl_sack.a_rwnd
+ if(vl_tsn_comp==0){
+ // No new TSN are ACK-ed.
+ if(( pl_sack.a_rwnd==0) and // the peers window is closed
+ (v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter == v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans) // retransmitting the zero probe, last trial
+ ){
+ // Don't let the retransmitt counter overflow
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter := v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter - 1
+ }
+ } else {
+ // stop the T3 timer, the SCTP_data_send_scheduler will restart it if needed
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+ }
+ // Something ack-ed -> try to send the remaining data
+ SCTP_data_send_scheduler(pl_assoc_id)
+
+ }
+
+ // TODO: handle gaps for fast retransmit
+
+ }
+ }
+ return 0
+
+}
+
+// Processes the cumulative TSN ack
+// return value:
+// -1: The acked TSN is less than the last acked
+// 0: The acked TSN is the same as the last acked
+// 1: New TSN is acked
+function SCTP_message_cumulative_ack_handler(in integer pl_ack, in integer pl_assoc_id) runs on SCTP_Engine_CT return integer {
+ var integer vl_tsn_comp:=SCTP_compare_tsn( pl_ack,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn)
+ if(vl_tsn_comp==-1){
+ // TSN already acked
+ // do nothing
+ return -1 //The acked TSN is less than the last acked
+ }
+ if(vl_tsn_comp==0){
+ // No new TSN are ACK-ed.
+ return 0
+ }
+ // New TSN's acked
+ // The remote side is alive, clear the retransmit counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter := 0
+
+ // remove the acked TSN-s from the tx chunk queue.
+ var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
+ var integer acked_bytes:=0
+ while((vl_idx!=-1) and (SCTP_compare_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].tsn,pl_ack)<1) ){
+ acked_bytes:=acked_bytes+SCTP_data_get_chunk_size(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].chunk)
+
+ // decrease the flight size
+ v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size-SCTP_data_get_chunk_size(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].chunk)
+ // remove the chunk
+ SCTP_Chunk_queue_pop(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue)
+ // next chunk idx
+ vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
+ }
+ //adjust the cwnd
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd<v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh){
+ // slow-start case
+ if((v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+acked_bytes) >= (v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd-c_min_chunk_size)){
+ // The cwnd is fully utilized
+ v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:= v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd + SCTP_min(acked_bytes,v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu)
+ }
+ } else {
+ // congestion avoidance case
+ v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes := v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes + acked_bytes
+ if((v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+acked_bytes) >= (v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd-c_min_chunk_size)){
+ // The cwnd is fully utilized
+ v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:= v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd + v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu
+ v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes := v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes - v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd
+ }
+
+ }
+
+ // Remember to the last ack-ed
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn:= pl_ack
+
+
+ return 1 // 1: New TSN is acked
+
+}
+
+// Handles the incoming DATA
+function SCTP_message_incoming_data_handler(in SCTP_Chunk pl_data, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+//log("data handler state, ", v_assoc_db.associations[pl_assoc_id].assoc_data.state)
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case (SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_SENT){
+ if(SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_data),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)>=0){
+ // put the chunk into the rx data chunk queue
+ SCTP_data_insert_into_rx(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue, // The queue
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn, // The first expected tsn of the queue, if empty
+ pl_data,
+ pl_assoc_id) // The chunk
+
+ } //else {
+ // The TSN is smaller than the last ack-ed
+ // just drop the chunk
+ // do nothing
+ //}
+//log("return 1")
+ return 1
+ }
+ }
+//log("return 0")
+ return 0
+}
+// Handles the incoming COOKIE-ECHO
+function SCTP_message_incoming_cookie_echo_handler(in SCTP_CookieEcho_chunk pl_cookie_echo, // The chunk to process
+ in integer pl_assoc_id, // The association
+ inout SCTP_Packet pl_response, // place the cookie echo into this message, if DATA chunk is also received, the SACK will be added later
+ in boolean pl_cookie_preprocessed
+ ) runs on SCTP_Engine_CT return integer {
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case (SCTP_ASSOC_ESTABLISHED){
+ // Process the messages and send answers if needed
+ if(pl_cookie_preprocessed){
+ // Only the cookie ack is should be sent
+ pl_response.chunks[lengthof(pl_response.chunks)]:={
+ cookie_ack:={
+ chunk_type := 11,
+ flags := '00000000'B,
+ chunk_length := 0
+ }
+ }
+ } else {
+ // TODO: Restart
+ }
+ }
+ }
+ return 0
+}
+// Handles the incoming COOKIE-ACK
+function SCTP_message_incoming_cookie_ack_handler(in SCTP_Cookie_ack_chunk pl_cookie_ack, // The chunk to process
+ in integer pl_assoc_id // The association
+ ) runs on SCTP_Engine_CT return integer {
+
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case (SCTP_ASSOC_COOKIE_ECHOED) {
+ v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_ESTABLISHED
+ // Start hearbeat
+ SCTP_hearbeat_start(pl_assoc_id)
+
+
+ // Stop the T1_cookie timer
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+
+ // Notify the user
+ var SCTP_Notification_data vl_notification:={
+ assoc_id := pl_assoc_id,
+ notification:= {comm_up := {}},
+ options := {
+ {i_data_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported},
+ {reconfig_params:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params},
+ {forward_tsn_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported}
+
+ }
+ }
+ SCTP_congestion_set_init_params(pl_assoc_id)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn := (v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn - 1) mod 4294967296
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+ // Reset the retransmit counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
+ }
+ }
+ return 0
+}
+// Handles the incoming INIT-ACK chunk
+function SCTP_message_incoming_init_ack_handler(in SCTP_InitAck_chunk pl_init_ack, // The chunk to process
+ in integer pl_assoc_id, // The association
+ in integer pl_chunk_idx // The index of the chunk in the incoming message
+ ) runs on SCTP_Engine_CT return integer {
+ select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
+ case (SCTP_ASSOC_COOKIE_WAIT) {
+
+ // Only the INIT ACK is acceptable
+ if( pl_chunk_idx == 0) {
+
+ // Find the cookie for echo
+ var integer vl_cookie_idx:=-1
+ var integer vl_supported_ext_idx:=-1
+ var integer vl_supported_forward_tsn_idx:=-1
+
+ if(ispresent(pl_init_ack.params)){
+ for(var integer vl_l:=0;vl_l<lengthof(pl_init_ack.params);vl_l:=vl_l+1){
+ if(ischosen(pl_init_ack.params[vl_l].state_cookie)){
+ vl_cookie_idx:=vl_l
+ }
+ if(ischosen(pl_init_ack.params[vl_l].supported_extensions)){
+ vl_supported_ext_idx:=vl_l
+ }
+ if(ischosen(pl_init_ack.params[vl_l].forward_tsn_supported)){
+ vl_supported_forward_tsn_idx:=vl_l
+ }
+
+ }
+ }
+
+ // Is there a cookie?
+ if(vl_cookie_idx==-1){
+ // No?
+ // Drop the packet
+ log("No cookie in the INIT_ACK. Dropped. ")
+ return -1;
+ }
+ // The INIT_ack seems OK
+
+ // Stop the timer
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+
+ // Update the database
+ v_assoc_db.associations[pl_assoc_id].assoc_data:={
+ state:=SCTP_ASSOC_COOKIE_ECHOED,
+ remote_tag:=pl_init_ack.init_tag,
+ remote_tsn:=pl_init_ack.init_tsn,
+ remote_os:=pl_init_ack.os,
+ remote_mis:=pl_init_ack.mis,
+ remote_a_rwnd:=pl_init_ack.a_rwnd,
+ state_cookie:=pl_init_ack.params[vl_cookie_idx].state_cookie.state_cookie,
+ reconf_remote_seq_num:=pl_init_ack.init_tsn
+ }
+
+ // Check the extensions support
+ var boolean vl_idata:=false
+ var boolean vl_reconf:=false
+ if(vl_supported_ext_idx!=-1){
+
+ for(var integer vl_i:=0;vl_i<lengthof(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions);vl_i:=vl_i+1){
+ if(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions[vl_i]==64){ // IDATA chunk
+ vl_idata:=true
+ }
+ if(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions[vl_i]==130){ // IDATA chunk
+ vl_reconf:=true
+ }
+ }
+ }
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported:=vl_idata and v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported:= vl_reconf and v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported
+ v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported:= (vl_supported_forward_tsn_idx!=-1) and v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported
+
+
+ // Send the Cookie Echo
+ SCTP_message_send_cookie_echo(pl_assoc_id)
+
+ // Reset the retransmit counter and start the timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T1_cookie,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
+ } else {
+ // Something else received
+ // Drop it
+ log("Unexpected packet, dorpped: ")
+ return -1
+ }
+ }
+ }
+
+ return 0
+}
+
+//Clean ups the acked and processed messages from the rx chunk queue
+function SCTP_data_clean_tx_chunk_queue(inout SCTP_Chunk_queue_db_t pl_queue, in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ var integer vl_chunk_idx:=pl_queue.first_element
+ while(vl_chunk_idx!=-1){
+ if( pl_queue.chunk_queue[vl_chunk_idx].mark_flag ){
+ // If the first element is marked, it is acked
+ SCTP_Chunk_queue_pop(pl_queue)
+
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked==vl_chunk_idx){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked:=-1
+ break; // We reached the last acked chunk
+ } else {
+ vl_chunk_idx:=pl_queue.first_element
+ }
+ } else {
+ break; // The chunk can not be removed from the incoming queue
+ }
+ }
+
+}
+
+// Delivers the whole messages to the user
+function SCTP_data_deliver(inout SCTP_msg_queue_list_t pl_msg_queue_list, in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ // Iterate through the rx message queues and deliver the messages
+ for(var integer vl_idx:=0; vl_idx<lengthof(pl_msg_queue_list);vl_idx:=vl_idx+1){
+ // The ordered queue
+ var integer vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
+ while(vl_msg_idx!=-1){
+ // check the messages in the stream queue
+ if(SCTP_message_get_E_bit(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk)=='1'B){
+ // Whole message
+ var SCTP_MSG_data vl_msg_data
+ SCTP_data_get_msg_data(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk,vl_msg_data,pl_assoc_id) // Copy the msg
+ // Deliver it
+ p_sctp_ind_api.call(S_SCTP_Received_ind:{vl_msg_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+ // Remove from the queue
+ SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages)
+
+ vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
+ } else {
+ if(not pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag){
+ break; // we reached the first not fully received message
+ } else {
+ // the msg are marked as skiped, skip it
+ SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages)
+
+ vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
+ }
+ }
+ }
+
+ // The unordered queue
+ vl_idx:=vl_idx+1
+ vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
+ while(vl_msg_idx!=-1){
+ // check the messages in the stream queue
+ if( (not pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag) // already processed
+ and (SCTP_message_get_E_bit(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk)=='1'B) // End flag
+ ){
+ // Whole message
+ var SCTP_MSG_data vl_msg_data
+ SCTP_data_get_msg_data(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk,vl_msg_data,pl_assoc_id) // Copy the msg
+ // Deliver it
+ p_sctp_ind_api.call(S_SCTP_Received_ind:{vl_msg_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ // Mark the msg
+ pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag:=true
+ }
+
+ // detect the skiped element
+ // For unordered message no direct signal of the skiped messages
+ // But if there is a next message and the tsn of the last fragement < acked tsn
+ // the message is skiped.
+ // Not allowed to interleave two unordered message of the same queue
+ if((pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].next_element!=-1)
+ and (SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0)){
+ // Mark the msg
+ pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag:=true
+ }
+
+ if(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag // already processed
+ and (vl_msg_idx==pl_msg_queue_list[vl_idx].messages.first_element) // first of the queue
+ ){
+
+ // remove it from the queue
+ SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages)
+ vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
+
+ } else {
+ vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].next_element
+ }
+
+ }
+ }
+
+}
+
+
+// Insert the chunk into the rx queue
+// pl_queue - the queue, where to insert the chunk
+// pl_expected_tsn - If the queue is empty the new first chunk should use this tsn
+// pl_chunk - the chunk to insert
+function SCTP_data_insert_into_rx(inout SCTP_Chunk_queue_db_t pl_queue, in integer pl_expected_tsn,in SCTP_Chunk pl_chunk, in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked
+ var integer vl_tsn:=pl_expected_tsn
+ var integer vl_chunk_tsn:=SCTP_data_get_chunk_tsn(pl_chunk)
+ // set the index to pint to the first not acked item
+ if(vl_idx==-1){
+ // the last_acked was removed from the queue
+ // take the first element
+ vl_idx:=pl_queue.first_element
+ } else {
+ // pick the next one
+ vl_tsn:=SCTP_next_tsn(pl_queue.chunk_queue[vl_idx].tsn) // set the tsn counter
+ vl_idx:=pl_queue.chunk_queue[vl_idx].next_element
+ }
+ // find the place of the item
+
+ while(true){
+ if(vl_idx==-1){
+ // We need to add item
+ SCTP_Chunk_queue_push(pl_queue,c_empty_Chunk_queue_element)
+ vl_idx:=pl_queue.last_element
+ pl_queue.chunk_queue[vl_idx].tsn:=vl_tsn
+ }
+ if(pl_queue.chunk_queue[vl_idx].tsn == vl_chunk_tsn){
+ break; // we found the place
+ }
+ // move to the next item
+ vl_idx:=pl_queue.chunk_queue[vl_idx].next_element
+ // take next tsn
+ vl_tsn:=SCTP_next_tsn(vl_tsn)
+ }
+
+ // store the chunk
+ pl_queue.chunk_queue[vl_idx].chunk:=pl_chunk
+
+}
+
+// Construct the messages from the rx chunk queue
+// scans the rx chunk queue for a messages
+
+function SCTP_data_chunk2msg(inout SCTP_Chunk_queue_db_t pl_queue, inout SCTP_msg_queue_list_t pl_msg_queue_list, in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ var integer vl_chunk_idx:=pl_queue.first_element
+ var boolean vl_gap_found:=false
+
+ while(vl_chunk_idx!=-1){
+ if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset)){
+ // See rfc6525 5.2.2. E2:
+ if(SCTP_compare_tsn(pl_queue.chunk_queue[vl_chunk_idx].tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset.last_assigned_tsn )>0){
+ break;
+ }
+ }
+
+
+ if(not pl_queue.chunk_queue[vl_chunk_idx].mark_flag){
+ // The chunk is not marked. Means was not put into the rx stream message queue
+ if(ispresent(pl_queue.chunk_queue[vl_chunk_idx].chunk)){
+ // Find the stream queue of the message
+ var integer vl_stream_idx:=-1; // index of the stream in the pl_msg_queue_list
+ var integer vl_stream_id:=SCTP_data_get_chunk_stream_id(pl_queue.chunk_queue[vl_chunk_idx].chunk) // The stream identifier
+ var integer vl_stream_seq_no
+ if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
+ vl_stream_id,
+ vl_stream_idx
+ )==-1){
+ // New incoming stream
+ // Is this the first message in the stream?
+ if( ((not vl_gap_found) // All of the previous chunk received, this is the first chunk of the first message
+ // of the stream. Or the remote side is sending a shit (or I forgot something)
+ or ( (SCTP_message_get_U_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk))=='1'B) ) // or first chunk of an unordered msg
+ and (SCTP_message_get_B_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk)=='1'B)
+ ){
+ vl_stream_idx:=lengthof(pl_msg_queue_list)
+ // ordered queue
+ pl_msg_queue_list[vl_stream_idx]:=c_empty_msg_queue;
+ pl_msg_queue_list[vl_stream_idx].next_ssn:=-1; // Not used queue
+
+ // undered queue
+ pl_msg_queue_list[vl_stream_idx+1]:=c_empty_msg_queue;
+ pl_msg_queue_list[vl_stream_idx+1].next_ssn:=-1; // Not used queue
+ if(SCTP_int2int_add(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
+ vl_stream_id,
+ vl_stream_idx) == -1){
+ log("Intenal error")
+ }
+ } else {
+ vl_stream_idx:=-1 // We can't be sure that this is the first message of the stream
+ }
+ }
+
+ if(vl_stream_idx!=-1){
+ // valid stream
+ if(SCTP_message_get_U_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk)=='0'B){
+ //ordered
+ if(pl_msg_queue_list[vl_stream_idx].next_ssn!=-1 or (not vl_gap_found)){
+ // In the case of the ordered message
+ // Try to store the first message if all of the previous chunk has been received.
+ SCTP_data_find_store_chunk(pl_msg_queue_list[vl_stream_idx],pl_queue.chunk_queue[vl_chunk_idx],false)
+ }
+ } else {
+ //unordered
+ SCTP_data_find_store_chunk(pl_msg_queue_list[vl_stream_idx+1],pl_queue.chunk_queue[vl_chunk_idx],true)
+ }
+ }
+
+ if(SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_queue.chunk_queue[vl_chunk_idx].chunk), v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0){
+ // if the tsn of the packet is already ack-ed mark it for clean up
+ pl_queue.chunk_queue[vl_chunk_idx].mark_flag:=true
+ }
+ } else {
+ // found a gap
+ vl_gap_found:=true
+ }
+ }
+ vl_chunk_idx:=pl_queue.chunk_queue[vl_chunk_idx].next_element
+ }
+}
+
+// Stores the message in the msg queue if applicable
+function SCTP_data_find_store_chunk(inout SCTP_msg_queue_t pl_msg_db, inout SCTP_Chunk_queue_element_t pl_chunk, in boolean pl_unordered){
+ if(((pl_msg_db.next_ssn==-1) or // not used queue
+ (pl_msg_db.next_ssn==SCTP_data_get_chunk_ssn(pl_chunk.chunk)) // next expected message
+ or pl_unordered) // Unordered
+ and
+ (SCTP_message_get_B_bit(pl_chunk.chunk)=='1'B) // First chunk of the message
+ ){
+ // unused queue, first chunk of the message
+ // or the first chunk of the next expected message
+ // Add the chunk as the first chunk of the next message
+ SCTP_Chunk_queue_push(pl_msg_db.messages,pl_chunk)
+ pl_chunk.mark_flag:=true // Chunk is in the rx stream message queue
+ pl_msg_db.next_ssn:=SCTP_next_ssn(SCTP_data_get_chunk_ssn(pl_chunk.chunk)) // Store the next ssn of the stream
+ pl_msg_db.messages.chunk_queue[pl_msg_db.messages.last_element].tsn:=SCTP_next_tsn(SCTP_data_get_chunk_reassembly_seq_no(pl_chunk.chunk)) // store the next TSN/FSN
+ return
+ }
+
+ // Find the message
+ var integer vl_idx:=pl_msg_db.messages.first_element
+ while(vl_idx!=-1){
+ if(SCTP_data_get_chunk_ssn(pl_msg_db.messages.chunk_queue[vl_idx].chunk) == SCTP_data_get_chunk_ssn(pl_chunk.chunk)){
+ // This is the message we're looking for
+ // Is this the next chunk?
+ if(pl_msg_db.messages.chunk_queue[vl_idx].tsn== SCTP_data_get_chunk_reassembly_seq_no(pl_chunk.chunk)){
+ // yes, this is
+ // append the data part.
+ SCTP_data_chunk_append_data(pl_msg_db.messages.chunk_queue[vl_idx].chunk,pl_chunk.chunk)
+
+ // updated the stored tsn of the message for support the RFC3758
+ SCTP_data_chunk_copy_tsn(pl_msg_db.messages.chunk_queue[vl_idx].chunk,pl_chunk.chunk)
+
+ pl_chunk.mark_flag:=true // Chunk is in the rx stream message queue
+
+ pl_msg_db.messages.chunk_queue[vl_idx].tsn := SCTP_next_tsn(pl_msg_db.messages.chunk_queue[vl_idx].tsn) // store th enext expected TSN/FSN
+ // Set E bit if needed
+ if(SCTP_message_get_E_bit(pl_chunk.chunk)=='1'B){
+ SCTP_message_set_E_bit('1'B,pl_msg_db.messages.chunk_queue[vl_idx].chunk)
+ }
+
+
+ }
+ return
+ }
+
+ vl_idx:=pl_msg_db.messages.chunk_queue[vl_idx].next_element
+
+ }
+
+
+}
+
+// Generates SACK from the rx queue
+// pl_queue - the queue, where to insert the chunk
+// pl_remote_tsn - The last acked tsn+1
+// pl_chunk - the chunk to return the SACK
+function SCTP_data_gen_sack(in SCTP_Chunk_queue_db_t pl_queue, in integer pl_remote_tsn,out SCTP_Chunk pl_chunk, in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked
+ // set the index to point to the first not acked item
+//log("*************************")
+//log(pl_queue)
+//log(pl_remote_tsn)
+//log(pl_assoc_id)
+//log("*************************")
+ if(vl_idx==-1){
+ // the last_acked was removed from the queue
+ // take the first element
+ vl_idx:=pl_queue.first_element
+ if(vl_idx!=-1 and
+ ((not ispresent(pl_queue.chunk_queue[vl_idx].chunk)) or pl_queue.chunk_queue[vl_idx].mark_flag )
+ ){
+ // The first element represents a non marked hole
+ vl_idx:=-1 // no new chunk to ack
+ }
+ }
+ // find the last element before a hole/end
+ while( (vl_idx != -1) and // valid element
+ (pl_queue.chunk_queue[vl_idx].next_element!=-1) and // has a next element
+ (ispresent(pl_queue.chunk_queue[pl_queue.chunk_queue[vl_idx].next_element].chunk) or pl_queue.chunk_queue[pl_queue.chunk_queue[vl_idx].next_element].mark_flag)// wich is not a hole
+ ){
+ vl_idx:=pl_queue.chunk_queue[vl_idx].next_element // advance to it
+ }
+
+ var integer vl_sack_tsn
+ if(vl_idx != -1){
+ vl_sack_tsn:=pl_queue.chunk_queue[vl_idx].tsn
+ } else {
+ vl_sack_tsn:= SCTP_prev_tsn(pl_remote_tsn)
+ }
+
+ pl_chunk.sack:={
+ chunk_type:= 3,
+ flags := '00000000'B,
+ chunk_length:=0,
+ cum_tsn_ack:=vl_sack_tsn,
+ a_rwnd:=65535,
+ no_of_gap_blocks:=0,
+ no_of_dup_tsn:=0,
+ gap_blocks:=omit,
+ dup_tsns:=omit
+ }
+
+ // set the pointers
+ v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked:=vl_idx
+ v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(vl_sack_tsn)
+}
+
+//Process the incoming INIT & COOKIE ECHO
+function SCTP_message_incoming_init_cookie_handler(in integer pl_assoc_id,
+ in integer pl_transport_id,
+ in SCTP_Packet pl_sctp_packet) runs on SCTP_Engine_CT {
+
+ if(ischosen(pl_sctp_packet.chunks[0].init)){
+ // INIT chunk received. Generate INIT-ACK
+ var SCTP_Packet vl_response := c_empty_sctp_packet
+ var SCTP_InitAck_chunk vl_initack_chunk := c_empty_init_ack_chunk
+ var integer vl_init_tag:=SCTP_rnd_32bit_int()
+
+ //Check the supported extensions
+ var boolean vl_idata:=false
+ var boolean vl_reconf:=false
+ var boolean vl_fwd_tsn:=false
+ if(ispresent(pl_sctp_packet.chunks[0].init.params)){
+ for(var integer vl_i:=0;vl_i<lengthof(pl_sctp_packet.chunks[0].init.params);vl_i:=vl_i+1){
+ if(ischosen(pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions)){
+ for(var integer vl_k:=0;vl_k<lengthof(pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions);vl_k:=vl_k+1){
+ if((pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions[vl_k]==64)){
+ vl_idata:=true
+ }
+ if((pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions[vl_k]==130)){
+ vl_reconf:=true
+ }
+ }
+
+ } else if(ischosen(pl_sctp_packet.chunks[0].init.params[vl_i].forward_tsn_supported)){
+ vl_fwd_tsn:=true
+ }
+ }
+ }
+ vl_idata:=vl_idata and v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported // support if requested both by the user and remote side
+
+ vl_reconf:=vl_reconf and v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported // support if requested both by the user and remote side
+
+ vl_fwd_tsn :=vl_fwd_tsn and v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported
+
+ var octetstring pl_cookie:=SCTP_gen_state_cookie(v_assoc_db.associations[pl_assoc_id].assoc_data.cookie_lifespan,
+ vl_init_tag,// pl_own_init_tag,
+ pl_sctp_packet.chunks[0].init.init_tag, // pl_remote_init_tag,
+ 0,// pl_tie_tag, 0 means no tie
+ pl_sctp_packet.chunks[0].init.init_tsn, // pl_remote_tsn,
+ pl_sctp_packet.chunks[0].init.os, // pl_remote_os,
+ pl_sctp_packet.chunks[0].init.mis ,// pl_remote_mis,
+ pl_sctp_packet.chunks[0].init.a_rwnd, // pl_remote_a_rwnd,
+ vl_idata,
+ vl_reconf,
+ vl_fwd_tsn ,
+ v_secret_key // pl_key
+ )
+ vl_initack_chunk:={
+ init_tag := vl_init_tag,
+ a_rwnd := 65535,
+ os := 65535,
+ mis := 65535,
+ init_tsn := vl_init_tag,
+ params :={
+ {state_cookie:={param_type:=7,param_length:=0,state_cookie:=pl_cookie}}
+ }
+ }
+ if(vl_idata or vl_reconf){
+ var integer vl_idx:=0
+ if(ispresent(vl_initack_chunk.params)){
+ vl_idx:=lengthof(vl_initack_chunk.params)
+ }
+ vl_initack_chunk.params[vl_idx].supported_extensions:={
+ param_type := 32776,
+ param_length :=0,
+ supported_extensions:={}
+ }
+ if(vl_idata){
+ vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=64
+ }
+ if(vl_reconf){
+ vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=130
+ }
+ }
+ if(vl_fwd_tsn){
+ var integer vl_idx:=0
+ if(ispresent(vl_initack_chunk.params)){
+ vl_idx:=lengthof(vl_initack_chunk.params)
+ }
+ vl_initack_chunk.params[vl_idx].forward_tsn_supported:={
+ param_type := 49152,
+ param_length :=0
+ }
+ }
+ vl_response:={
+ common_header := {
+ source_port:=pl_sctp_packet.common_header.destination_port,
+ destination_port:=pl_sctp_packet.common_header.source_port,
+ verification_tag:=pl_sctp_packet.chunks[0].init.init_tag
+ },
+ chunks := { {init_ack:=vl_initack_chunk} }
+ }
+ SCTP_send_packet(pl_transport_id,vl_response);
+
+ } else {
+ // Cookie Echo received, process it.
+ // Allocate new assoc
+ var integer vl_new_assoc:=SCTP_assoc_construct_from_cookie_echo(pl_sctp_packet,pl_transport_id,pl_assoc_id);
+
+ if(vl_new_assoc < 0){ // The cookie check failed
+ // drop the packet
+ return;
+ }
+
+ // Check the cookie lifetime
+ if(v_assoc_db.associations[vl_new_assoc].assoc_data.cookie_validity==1){ // Cookie expired
+ // Send abort with erro chunk
+
+ // Construct the error chunk
+ var SCTP_error_chunk vl_error_chunk:= {
+ chunk_type := 9,
+ flags := '00000000'B,
+ chunk_length := 0,
+ params := {
+ {stale_cookie:={cause_code:=3, cause_length:=0,measure:=0}}
+ }
+ }
+
+ // send the abort
+ SCTP_message_send_abort(pl_transport_id,pl_sctp_packet,vl_new_assoc,-,{{error_:=vl_error_chunk}})
+
+ // drop the association
+ SCTP_free_assoc_entry(vl_new_assoc)
+ return; // We're done
+ }
+ // Add the connection to the id map
+ if(SCTP_add_id_map(vl_new_assoc,pl_transport_id,v_assoc_db.associations[vl_new_assoc].assoc_data.local_port,v_assoc_db.associations[vl_new_assoc].assoc_data.remote_port)==-1){
+ // Stop further processing
+ return;
+ }
+
+ // Send connected
+ var SCTP_Connected_data vl_connected:={
+ assoc_id := vl_new_assoc,
+ local_sctp_port := v_assoc_db.associations[vl_new_assoc].assoc_data.local_port,
+ remote_sctp_port := v_assoc_db.associations[vl_new_assoc].assoc_data.remote_port,
+ listen_assoc_id := pl_assoc_id
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Connected_ind:{vl_connected},nowait) to v_assoc_db.associations[vl_new_assoc].assoc_data.rem_comp
+
+ // Send notification to the owner of the listen port
+ var SCTP_Notification_data vl_notification:={
+ assoc_id := vl_new_assoc,
+ notification:= {comm_up := {}},
+ options := {
+ {i_data_supported:=v_assoc_db.associations[vl_new_assoc].assoc_data.i_data_supported},
+ {reconfig_params:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params},
+ {forward_tsn_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported}
+
+ }
+ }
+
+
+
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[vl_new_assoc].assoc_data.rem_comp
+ v_assoc_db.associations[vl_new_assoc].assoc_data.state:=SCTP_ASSOC_ESTABLISHED;
+ SCTP_hearbeat_start(vl_new_assoc)
+
+ // Reset the retransmit counter
+ v_assoc_db.associations[vl_new_assoc].assoc_data.retransmit_counter:=0
+
+ SCTP_congestion_set_init_params(vl_new_assoc)
+ // Set the last acked TSN to the expeted -1
+ v_assoc_db.associations[vl_new_assoc].assoc_data.remote_last_sack_tsn := (v_assoc_db.associations[vl_new_assoc].assoc_data.own_tsn - 1) mod 4294967296
+
+ // Send the packet for further processing
+ SCTP_message_incoming_handler(vl_new_assoc,pl_sctp_packet,true)
+
+
+
+ }
+}
+
+// Process the Out of the blue packets
+// See 8.4 of rfc
+function SCTP_message_process_oob_packet(in integer pl_transport_id, // Where to send the abort
+ in SCTP_Packet pl_sctp_packet // The incoming packet
+ ) runs on SCTP_Engine_CT {
+// first search map the chunks
+// Interested ones:
+// - Abort
+ var boolean vl_abort_found:=false
+// - Init
+ var boolean vl_init_found:=false
+// - Cookie Echo
+ var boolean vl_cookie_echo_found:=false
+// - Shutdown Complete
+ var boolean vl_shutdown_complete_found:=false
+// - Shutdown Ack
+ var boolean vl_shutdown_ack_found:=false
+// - CookieAck
+ var boolean vl_cookie_ack_found:=false
+
+ var integer vl_i:=0
+ for(vl_i:=0;vl_i<lengthof(pl_sctp_packet.chunks);vl_i:=vl_i+1) {
+ if(ischosen(pl_sctp_packet.chunks[vl_i].abort)){
+ vl_abort_found:=true
+ continue
+ } else if (ischosen(pl_sctp_packet.chunks[vl_i].init)) {
+ vl_init_found:=true
+ continue
+ } else if (ischosen(pl_sctp_packet.chunks[vl_i].cookie_echo)) {
+ vl_cookie_echo_found:=true
+ continue
+ } else if (ischosen(pl_sctp_packet.chunks[vl_i].shutdown_complete)) {
+ vl_shutdown_complete_found:=true
+ continue
+ } else if (ischosen(pl_sctp_packet.chunks[vl_i].shutdown_ack)) {
+ vl_shutdown_ack_found:=true
+ continue
+ } else if (ischosen(pl_sctp_packet.chunks[vl_i].cookie_ack)) {
+ vl_cookie_ack_found:=true
+ continue
+ }
+ }
+
+// The packet should be dropped if
+// - Abort
+// - CookieAck
+// - Shutdown Complete
+ if( vl_abort_found or vl_cookie_ack_found or vl_shutdown_complete_found){
+ return; // do nothing
+ } else if(vl_init_found){
+// Init -> send abort - tag handled automatically
+ SCTP_message_send_abort(pl_transport_id,pl_sctp_packet)
+ } else if(vl_cookie_echo_found) {
+// Cookie Echo -> Send abort, but check the cookie first
+ // construct a temp assoc entry, it checks the validity of the cookie also
+ var integer pl_temp_id:=SCTP_assoc_construct_from_cookie_echo(pl_sctp_packet,pl_transport_id);
+
+ if(pl_temp_id>=0){
+ // send abort
+ SCTP_message_send_abort(pl_transport_id,-,pl_temp_id);
+ // free the assoc entry
+ SCTP_free_assoc_entry(pl_temp_id);
+ }
+ // else just drop the message
+ } else if(vl_shutdown_ack_found) {
+// SHUTDOWN ACK -> send SHUTDOWN COMPLETE
+ SCTP_message_send_shutdown_complete(pl_transport_id,pl_sctp_packet)
+ } else {
+ // any other case: send abort
+ SCTP_message_send_abort(pl_transport_id,pl_sctp_packet)
+ }
+
+}
+
+
+
+// Send the shutdown ack message
+function SCTP_message_send_shutdown_ack(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+
+ var SCTP_Packet vl_sctp := c_empty_sctp_packet
+ vl_sctp:={
+ common_header:={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ },
+ chunks:={{shutdown_ack:={chunk_type:=8,flags:='00000000'B,chunk_length:=0}}}
+ }
+
+
+ // Send it
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
+
+
+}
+
+// Send the shutdown message
+function SCTP_message_send_shutdown(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+
+ var SCTP_Packet vl_sctp := c_empty_sctp_packet
+ vl_sctp:={
+ common_header:={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ },
+ chunks:={{shutdown:={chunk_type:=7,flags:='00000000'B,chunk_length:=0,cum_tsn_ack:=SCTP_prev_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)}}}
+ }
+
+
+ // Send it
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
+
+
+}
+
+function SCTP_message_send_heartbeat(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ var SCTP_Packet vl_sctp := c_empty_sctp_packet
+ vl_sctp:={
+ common_header:={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ },
+ chunks:={{heartbeat:={chunk_type:=4,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={{heartbeat_info:={ param_type:=1,
+ param_length := 0,
+ info:=SCTP_misc_float2oct(t_run_time.read)
+ }}}
+ }}}
+ }
+
+
+ // Send it
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
+
+}
+
+// Send abort message
+// The abort message can be constructed from
+// -- assoc data
+// -- incoming sctp packet
+function SCTP_message_send_abort(in integer pl_transport_id:=-1, // Where to send the abort
+ in SCTP_Packet pl_sctp_packet := c_empty_sctp_packet, // The incoming packet caused the abort
+ in integer pl_assoc_id:=-1, // The assoc id if applicable
+ in SCTP_Error_cause_list pl_cause_list:= {}, // the cause the abort
+ in SCTP_Chunk_List pl_chunk_list :={} // The chunks include before the abort chunk
+ ) runs on SCTP_Engine_CT {
+
+ var SCTP_Packet vl_response := c_empty_sctp_packet
+ var SCTP_abort_chunk vl_abort_chunk := c_empty_abort_chunk
+
+ if(pl_assoc_id != -1){
+ // We have association
+ // Use the data from the database
+ pl_transport_id := v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id
+ vl_response.common_header:={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ }
+ vl_abort_chunk.T_flag:='0'B // The verification_tag is not reflected
+ } else {
+ // Out of the blue packet
+ if( (lengthof(pl_sctp_packet.chunks) > 0)
+ and ischosen(pl_sctp_packet.chunks[0].init)
+ ){
+ // INIT chunk, get the Initiate Tag for the verification_tag
+ vl_response.common_header:={
+ source_port:=pl_sctp_packet.common_header.destination_port,
+ destination_port:=pl_sctp_packet.common_header.source_port,
+ verification_tag:=pl_sctp_packet.chunks[0].init.init_tag
+ }
+ vl_abort_chunk.T_flag:='0'B // The verification_tag is not reflected
+ } else {
+ // reflect the verification_tag
+ vl_response.common_header:={
+ source_port:=pl_sctp_packet.common_header.destination_port,
+ destination_port:=pl_sctp_packet.common_header.source_port,
+ verification_tag:=pl_sctp_packet.common_header.verification_tag
+ }
+ vl_abort_chunk.T_flag:='1'B // The verification_tag is reflected
+ }
+
+ }
+
+ // Construct the message
+
+ // Add cause list if needed
+ if(lengthof(pl_cause_list) > 0){
+ vl_abort_chunk.params:=pl_cause_list
+ }
+
+ // Add the other chunks before the abort chunk
+ if(lengthof(pl_chunk_list) > 0){
+ vl_response.chunks:=pl_chunk_list
+ }
+
+ // Add the abort chunk
+ vl_response.chunks[lengthof(vl_response.chunks)].abort:=vl_abort_chunk;
+
+ // Send it
+ SCTP_send_packet(pl_transport_id,vl_response);
+
+}
+
+// Send abort message
+// The shutdown complete message can be constructed from
+// -- assoc data
+// -- incoming sctp packet
+function SCTP_message_send_shutdown_complete(in integer pl_transport_id:=-1, // Where to send
+ in SCTP_Packet pl_sctp_packet := c_empty_sctp_packet, // The incoming packet
+ in integer pl_assoc_id:=-1 // The assoc id if applicable
+ ) runs on SCTP_Engine_CT {
+
+ var SCTP_Packet vl_response := c_empty_sctp_packet
+ var SCTP_ShutdownComplete_chunk vl_sh_comp_chunk := c_empty_shutdown_complete_chunk
+
+ if(pl_assoc_id != -1){
+ // We have association
+ // Use the data from the database
+ pl_transport_id := v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id
+ vl_response.common_header:={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ }
+ vl_sh_comp_chunk.T_flag:='0'B // The verification_tag is not reflected
+ } else {
+ // Out of the blue packet
+ // reflect the verification_tag
+ vl_response.common_header:={
+ source_port:=pl_sctp_packet.common_header.destination_port,
+ destination_port:=pl_sctp_packet.common_header.source_port,
+ verification_tag:=pl_sctp_packet.common_header.verification_tag
+ }
+ vl_sh_comp_chunk.T_flag:='1'B // The verification_tag is reflected
+ }
+
+
+ // Construct the message
+
+ // Add the chunk
+ vl_response.chunks[lengthof(vl_response.chunks)].shutdown_complete:=vl_sh_comp_chunk;
+
+ // Send it
+ SCTP_send_packet(pl_transport_id,vl_response);
+
+}
+
+// Send the sctp packet
+function SCTP_send_packet(in integer pl_transport_id, // Where to send the packet
+ in SCTP_Packet pl_sctp_packet) runs on SCTP_Engine_CT {
+
+ var SCTP_Engine_packet vl_packet:={
+ transport_id:=pl_transport_id,
+ sctp_packet:=f_SCTP_enc(pl_sctp_packet)
+ }
+
+ p_packet_port.send(vl_packet)
+}
+
+// Send message with Cookie Echo
+function SCTP_message_send_cookie_echo(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+
+ var SCTP_Packet vl_sctp := c_empty_sctp_packet
+ vl_sctp:={
+ common_header :={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag := v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ },
+ chunks:={{cookie_echo:={chunk_type:=10, flags:= '00000000'B, chunk_length:=0,cookie:=v_assoc_db.associations[pl_assoc_id].assoc_data.state_cookie}}}
+ }
+
+ // Send the packet
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp)
+}
+/*****************************************************************************/
+//
+// API functions
+//
+/*****************************************************************************/
+
+// Handles the reconfig request
+function SCTP_api_handle_reconfig(in SCTP_Reconfiguration_req vl_reconfig, // the listen parameters
+ in SCTP_Engine_CT vl_sender // The sender of the request
+ ) runs on SCTP_Engine_CT {
+
+ var integer vl_assoc_id:=vl_reconfig.assoc_id
+ // Check the associ id validity
+
+ if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary
+ or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used?
+ or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc?
+ ){
+ // invalid assoc_id
+ p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender
+ return
+ }
+
+ // check the state of the association
+ if(v_assoc_db.associations[vl_assoc_id].assoc_data.state!=SCTP_ASSOC_ESTABLISHED){
+ // The sendrequest is allowed only in established state
+ p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is not in the established state"}) to vl_sender
+ return
+
+ }
+
+ // Check if request is ongoing
+ if(v_assoc_db.associations[vl_assoc_id].assoc_data.reconfig_ongoing){
+ p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_RECONFIG_ONGOING,"There is an ongoing reconfig request. Please try again later"}) to vl_sender
+ return
+
+ }
+
+ var SCTP_Packet vl_sctp := c_empty_sctp_packet
+ vl_sctp:={
+ common_header :={
+ source_port:=v_assoc_db.associations[vl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[vl_assoc_id].assoc_data.remote_port,
+ verification_tag := v_assoc_db.associations[vl_assoc_id].assoc_data.remote_tag
+ },
+ chunks:={{
+ re_config:={
+ chunk_type:=130,
+ flags:='00000000'B,
+ chunk_length:=0,
+ params:={}
+
+ }
+ }}
+ }
+
+ // create and send the request
+ if(ischosen(vl_reconfig.reconf_method.assoc_reset)){
+ vl_sctp.chunks[0].re_config.params[0]:={
+ ssn_tsn_reset_req:={
+ param_type:=15,
+ param_length:=0,
+ reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
+ }
+ }
+
+ v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
+
+ } else if(ischosen(vl_reconfig.reconf_method.stream_reset)){
+ if(vl_reconfig.reconf_method.stream_reset.incoming){
+ vl_sctp.chunks[0].re_config.params[0]:={
+ in_ssn_reset_req:={
+ param_type:=14,
+ param_length:=0,
+ reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num,
+ stream_numbers:=vl_reconfig.reconf_method.stream_reset.streams
+ }
+ }
+ v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
+
+ } else {
+ if(ispresent(vl_reconfig.reconf_method.stream_reset.streams)){
+ SCTP_reconf_generate_out_ssn_reset_req( vl_reconfig.reconf_method.stream_reset.streams,
+ SCTP_prev_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.own_tsn),
+ vl_sctp.chunks[0].re_config,
+ vl_assoc_id
+ )
+ } else {
+ SCTP_reconf_generate_out_ssn_reset_req( {},
+ SCTP_prev_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.own_tsn),
+ vl_sctp.chunks[0].re_config,
+ vl_assoc_id
+ )
+ }
+ v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
+ }
+
+ } else {
+ // add stream req
+ if(vl_reconfig.reconf_method.add_stream.incoming){
+ vl_sctp.chunks[0].re_config.params[0]:={
+ add_in_stream:={
+ param_type:=18,
+ param_length:=0,
+ reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num,
+ stream_num:=vl_reconfig.reconf_method.add_stream.new_streams,
+ reserved:=0
+ }
+ }
+ v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
+ } else {
+ vl_sctp.chunks[0].re_config.params[0]:={
+ add_out_stream:={
+ param_type:=17,
+ param_length:=0,
+ reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num,
+ stream_num:=vl_reconfig.reconf_method.add_stream.new_streams,
+ reserved:=0
+ }
+ }
+ v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
+ }
+
+ }
+
+ v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num:=SCTP_next_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num)
+
+ // Send the packet
+ SCTP_send_packet(v_assoc_db.associations[vl_assoc_id].assoc_data.transport_id,vl_sctp)
+
+}
+
+// Handles the shutdown_conf request
+function SCTP_api_handle_shutdown_conf (in integer vl_assoc_id, // the listen parameters
+ in SCTP_Engine_CT vl_sender // The sender of the request
+ ) runs on SCTP_Engine_CT {
+
+ if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary
+ or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used?
+ or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc?
+ ){
+ // invalid assoc_id
+ p_sctp_req_api.raise(S_SCTP_Shutdown_conf, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender
+ return
+ }
+
+ // check the state of the association
+ if(v_assoc_db.associations[vl_assoc_id].assoc_data.state!=SCTP_ASSOC_CLOSED){
+ // The sendrequest is allowed only in established state
+ p_sctp_req_api.raise(S_SCTP_Shutdown_conf, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is not in closed state"}) to vl_sender
+ return
+
+ }
+
+ // Free the association
+ SCTP_free_assoc_entry(vl_assoc_id)
+
+ // Confirm the request
+ p_sctp_req_api.reply(S_SCTP_Shutdown_conf:{?} value {vl_assoc_id}) to vl_sender
+
+}
+// Handles the shutdown request
+function SCTP_api_handle_shutdown (in SCTP_Shutdown_data pl_shutdown_data, // the shutdown parameters
+ in SCTP_Engine_CT vl_sender // The sender of the request
+ ) runs on SCTP_Engine_CT {
+
+ var integer vl_assoc_id:=pl_shutdown_data.assoc_id
+ // Check the associ id validity
+
+ if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary
+ or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used?
+ or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc?
+ ){
+ // invalid assoc_id
+ p_sctp_req_api.raise(S_SCTP_Shutdown, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender
+ return
+ }
+
+ // check the state of the association
+ if(v_assoc_db.associations[vl_assoc_id].assoc_data.state==SCTP_ASSOC_CLOSED){
+ // The sendrequest is allowed only in established state
+ p_sctp_req_api.raise(S_SCTP_Shutdown, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is already in closed state"}) to vl_sender
+ return
+
+ }
+
+ if(not pl_shutdown_data.graceful_shutdown){
+ // Abort was requested
+ if(ispresent(pl_shutdown_data.reason)){
+ // send the abort with the reason
+ SCTP_message_send_abort(-,-,vl_assoc_id,{{user_abort:={cause_code:=12, cause_length:=0,reason:=char2oct(pl_shutdown_data.reason)}}})
+ } else {
+ SCTP_message_send_abort(-,-,vl_assoc_id)
+ }
+ // Confirm the request
+ p_sctp_req_api.reply(S_SCTP_Shutdown:{?} value {vl_assoc_id}) to vl_sender
+
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=vl_assoc_id,
+ graceful_shutdown := false,
+ reason := "Abort request has been sent."
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(vl_assoc_id)
+
+ } else {
+ select(v_assoc_db.associations[vl_assoc_id].assoc_data.state){
+ case ( (SCTP_ASSOC_COOKIE_WAIT,SCTP_ASSOC_COOKIE_ECHOED)){
+ // We're in th eset up phase
+ // Abort it
+ SCTP_message_send_abort(-,-,vl_assoc_id)
+ // Confirm the request
+ p_sctp_req_api.reply(S_SCTP_Shutdown:{?} value {vl_assoc_id}) to vl_sender
+
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=vl_assoc_id,
+ graceful_shutdown := false,
+ reason := "Association set up has been aborted."
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(vl_assoc_id)
+
+ }
+ case (SCTP_ASSOC_ESTABLISHED ){
+ // Set the state to shutdown pending
+ v_assoc_db.associations[vl_assoc_id].assoc_data.state:=SCTP_ASSOC_SHUTDOWN_PENDING
+
+ // Confirm the request
+ p_sctp_req_api.reply(S_SCTP_Shutdown:{?} value {vl_assoc_id}) to vl_sender
+
+ // Call the data scheduler. It will send the SHUTDOWN if possible
+ SCTP_data_send_scheduler(vl_assoc_id)
+ }
+
+ case else {
+ // We're already in the shutdown phase
+ // Confirm the request
+ p_sctp_req_api.reply(S_SCTP_Shutdown:{?} value {vl_assoc_id}) to vl_sender
+ // Nothing more to do here
+ }
+ }
+
+ }
+
+}
+
+
+// Handles the send msg request
+function SCTP_api_handle_send_msg (in SCTP_MSG_data pl_send_msg_data, // the send req parameters
+ in SCTP_Engine_CT vl_sender // The sender of the request
+ ) runs on SCTP_Engine_CT {
+
+ var integer vl_assoc_id:=pl_send_msg_data.assoc_id
+ // Check the associ id validity
+
+ if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary
+ or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used?
+ or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc?
+ ){
+ // invalid assoc_id
+ p_sctp_req_api.raise(S_SCTP_Send_req, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender
+ return
+ }
+
+ // check the state of the association
+ if(v_assoc_db.associations[vl_assoc_id].assoc_data.state!=SCTP_ASSOC_ESTABLISHED){
+ // The sendrequest is allowed only in established state
+ p_sctp_req_api.raise(S_SCTP_Send_req, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is not in the established state"}) to vl_sender
+ return
+
+ }
+
+ // put the message into th estream queue
+
+ var integer vl_stream_id:=pl_send_msg_data.stream_id
+ var integer vl_queue_id
+
+ // get the queue id
+ if(SCTP_int2int_get(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_stream_idx_map_id,
+ vl_stream_id,
+ vl_queue_id
+ )==-1){
+ // No queue for that stream
+ // create one
+
+ vl_queue_id:=lengthof(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues)
+ // ordered queue
+ v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id]:=c_empty_msg_queue
+ // unordered queue
+ v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id+1]:=c_empty_msg_queue
+
+ // register it in the map
+ if(SCTP_int2int_add(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_stream_idx_map_id,
+ vl_stream_id,
+ vl_queue_id) == -1){
+ // WTF???
+ p_sctp_req_api.raise(S_SCTP_Send_req, SCTP_operation_execption:{SCTP_ENG_INTERNAL_ERROR,"INTERNAL Error: wrong map id SCTP_api_handle_send_msg SCTP_int2int_add"}) to vl_sender
+ return
+
+ }
+ }
+
+ var boolean vl_ordered:= true
+ var bitstring vl_uflag:= '0'B
+ // Put the msg into that queue
+ var SCTP_Chunk_queue_element_t vl_msg_element:=c_empty_Chunk_queue_element
+
+ // Is it unordered?
+ if(ispresent(pl_send_msg_data.options)){
+ for(var integer vl_x:=0;vl_x<lengthof(pl_send_msg_data.options); vl_x:=vl_x+1){
+ if(ischosen(pl_send_msg_data.options[vl_x].unordered)){
+ vl_ordered:= false // not ordered
+ vl_queue_id:=vl_queue_id+1 // adjust the queue id
+ vl_uflag:= '1'B
+ } else if(ischosen(pl_send_msg_data.options[vl_x].lifetime)){
+ vl_msg_element.lifetime:=t_run_time.read+pl_send_msg_data.options[vl_x].lifetime
+ } else if(ischosen(pl_send_msg_data.options[vl_x].max_retransmit_counter)){
+ vl_msg_element.counter:=pl_send_msg_data.options[vl_x].max_retransmit_counter
+ }
+ }
+ }
+
+
+
+ if(v_assoc_db.associations[vl_assoc_id].assoc_data.i_data_supported){
+ // Use IData
+ vl_msg_element:={
+ chunk:={
+ idata:={
+ chunk_type:= 64,
+ E_flag := '0'B,
+ B_flag := '0'B,
+ U_flag := vl_uflag,
+ I_flag := '0'B,
+ res_flags := '0000'B,
+ chunk_length := 0,
+ tsn := 0,
+ stream_id := vl_stream_id,
+ reserved := 0,
+ mid := v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn,
+ ppid_fsn := pl_send_msg_data.ppid,
+ user_data := pl_send_msg_data.data
+ }
+ }
+ }
+ // next_ssn means: next message id
+ v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn :=SCTP_next_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn)
+ } else {
+ // Normal DATA chunk
+ vl_msg_element:={
+ chunk:={
+ data:={
+ chunk_type:= 0,
+ E_flag := '0'B,
+ B_flag := '0'B,
+ U_flag := vl_uflag,
+ I_flag := '0'B,
+ res_flags := '0000'B,
+ chunk_length := 0,
+ tsn := 0,
+ stream_id := vl_stream_id,
+ ssn := v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn,
+ ppid := pl_send_msg_data.ppid,
+ user_data := pl_send_msg_data.data
+ }
+ }
+ }
+ // Adjust the next ssn
+ if(vl_ordered){
+ // The unordered doesn't have ssn
+ v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn :=SCTP_next_ssn(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn)
+ }
+ }
+
+
+ SCTP_Chunk_queue_push(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].messages,vl_msg_element)
+
+
+ // send back the result
+ p_sctp_req_api.reply(S_SCTP_Send_req:{?} value {vl_assoc_id}) to vl_sender
+
+
+ // Invoke the send scheduler
+ SCTP_data_send_scheduler(vl_assoc_id)
+}
+// Handles the listen request
+function SCTP_api_handle_listen(in SCTP_Listen_data pl_listen_data, // the listen parameters
+ in SCTP_Engine_CT vl_sender // The sender of the request
+ ) runs on SCTP_Engine_CT {
+// Basic check
+ if((pl_listen_data.local_sctp_port<1) or (pl_listen_data.local_sctp_port>65535)){
+ // The listen port is out of the range
+ p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_WRONG_PORT,"Listen port is out of the allowed range 1..65535"}) to vl_sender
+ // Stop further processing
+ return;
+ }
+
+ // Check transport id
+ if(pl_listen_data.transport_id<-1){
+ // The transport id should be >=0 or -1 (all connection)
+ p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_WRONG_PARAMETERS,"The transport id should be >=0 or -1 (all connection)"}) to vl_sender
+ // Stop further processing
+ return;
+ }
+
+ // Is the port is used?
+ if(SCTP_find_id_map(pl_listen_data.transport_id,pl_listen_data.local_sctp_port,0)!=-1){
+ // The port is already used for listen
+ p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_PORT_ALREADY_USED,"The port is already used."}) to vl_sender
+ // Stop further processing
+ return;
+
+ }
+
+ // create a new assoc entry
+ var integer vl_new_assoc:=SCTP_allocate_assoc_entry();
+
+ // Fill the data
+ v_assoc_db.associations[vl_new_assoc].assoc_data:={
+ state := SCTP_ASSOC_LISTEN,
+ transport_id :=pl_listen_data.transport_id ,
+ local_port:=pl_listen_data.local_sctp_port,
+ remote_port:=0,
+ rem_comp:=vl_sender
+ }
+
+ // Add it to the map
+ if(SCTP_add_id_map(vl_new_assoc,pl_listen_data.transport_id,pl_listen_data.local_sctp_port,0)==-1){
+ // Something went wrong
+ // free the assoc entry
+ SCTP_free_assoc_entry(vl_new_assoc);
+ // Raise the exception
+ p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_INTERNAL_ERROR,"Internal error. SCTP_add_id_map -1"}) to vl_sender
+ // Stop further processing
+ return;
+ }
+
+ // process the options
+ if(ispresent(pl_listen_data.options)){
+ var integer k
+ for(k:=0;k<lengthof(pl_listen_data.options);k:=k+1){
+ if(ischosen(pl_listen_data.options[k].i_data_supported)){
+ v_assoc_db.associations[vl_new_assoc].assoc_data.i_data_supported:=pl_listen_data.options[k].i_data_supported
+ }
+ if(ischosen(pl_listen_data.options[k].forward_tsn_supported)){
+ v_assoc_db.associations[vl_new_assoc].assoc_data.forward_tsn_supported:=pl_listen_data.options[k].forward_tsn_supported
+ }
+ if(ischosen(pl_listen_data.options[k].reconfig_params)){
+ v_assoc_db.associations[vl_new_assoc].assoc_data.reconf_params:=pl_listen_data.options[k].reconfig_params
+ }
+ }
+ }
+
+ // return the result
+ p_sctp_req_api.reply(S_SCTP_Listen:{?} value {vl_new_assoc}) to vl_sender
+
+}
+
+// Handles the connect request
+function SCTP_api_handle_connect(in SCTP_Connect_data pl_connect_data , // the connect parameters
+ in SCTP_Engine_CT vl_sender // The sender of the request
+ ) runs on SCTP_Engine_CT {
+// Check the port numbers
+ if((pl_connect_data.local_sctp_port<1) or (pl_connect_data.local_sctp_port>65535)){
+ // The listen port is out of the range
+ p_sctp_req_api.raise(S_SCTP_Connect, SCTP_operation_execption:{SCTP_ENG_WRONG_PORT,"Listen port is out of the allowed range 1..65535"}) to vl_sender
+ // Stop further processing
+ return;
+ }
+ if((pl_connect_data.remote_sctp_port<1) or (pl_connect_data.remote_sctp_port>65535)){
+ // The listen port is out of the range
+ p_sctp_req_api.raise(S_SCTP_Connect, SCTP_operation_execption:{SCTP_ENG_WRONG_PORT,"Listen port is out of the allowed range 1..65535"}) to vl_sender
+ // Stop further processing
+ return;
+ }
+
+ // Check transport id
+ if(pl_connect_data.transport_id<0){
+ // The transport id should be >=0
+ p_sctp_req_api.raise(S_SCTP_Connect, SCTP_operation_execption:{SCTP_ENG_WRONG_PARAMETERS,"The transport id should be >=0"}) to vl_sender
+ // Stop further processing
+ return;
+ }
+
+ // Is the port is used?
+ if(SCTP_find_id_map(pl_connect_data.transport_id,pl_connect_data.local_sctp_port,pl_connect_data.remote_sctp_port)!=-1){
+ // The port is already used for listen
+ p_sctp_req_api.raise(S_SCTP_Connect, SCTP_operation_execption:{SCTP_ENG_PORT_ALREADY_USED,"The port is already used."}) to vl_sender
+ // Stop further processing
+ return;
+ }
+
+ // create a new assoc entry
+ var integer vl_new_assoc:=SCTP_allocate_assoc_entry();
+
+ // Fill the data
+ v_assoc_db.associations[vl_new_assoc].assoc_data:={
+ state := SCTP_ASSOC_COOKIE_WAIT,
+ transport_id :=pl_connect_data.transport_id ,
+ local_port:=pl_connect_data.local_sctp_port,
+ remote_port:=pl_connect_data.remote_sctp_port,
+ own_tag:=SCTP_rnd_32bit_int(),
+ rem_comp:=vl_sender
+ }
+ v_assoc_db.associations[vl_new_assoc].assoc_data.own_tsn:=v_assoc_db.associations[vl_new_assoc].assoc_data.own_tag
+ v_assoc_db.associations[vl_new_assoc].assoc_data.reconf_own_seq_num:=v_assoc_db.associations[vl_new_assoc].assoc_data.own_tag
+
+ // process the options
+ if(ispresent(pl_connect_data.options)){
+ var integer k
+ for(k:=0;k<lengthof(pl_connect_data.options);k:=k+1){
+ if(ischosen(pl_connect_data.options[k].i_data_supported)){
+ v_assoc_db.associations[vl_new_assoc].assoc_data.i_data_supported:=pl_connect_data.options[k].i_data_supported
+ }
+ if(ischosen(pl_connect_data.options[k].forward_tsn_supported)){
+ v_assoc_db.associations[vl_new_assoc].assoc_data.forward_tsn_supported:=pl_connect_data.options[k].forward_tsn_supported
+ }
+ if(ischosen(pl_connect_data.options[k].reconfig_params)){
+ v_assoc_db.associations[vl_new_assoc].assoc_data.reconf_params:=pl_connect_data.options[k].reconfig_params
+ }
+ }
+ }
+
+ // Add the assoc to the id map
+ if(SCTP_add_id_map(vl_new_assoc,pl_connect_data.transport_id,pl_connect_data.local_sctp_port,pl_connect_data.remote_sctp_port)==-1){
+ // Something went wrong
+ // free the assoc entry
+ SCTP_free_assoc_entry(vl_new_assoc);
+ // Raise the exception
+ p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_INTERNAL_ERROR,"Internal error. SCTP_add_id_map -1"}) to vl_sender
+ // Stop further processing
+ return;
+ }
+
+ // Send the INIT
+ SCTP_message_send_init(vl_new_assoc)
+
+ // Start timer
+ v_assoc_db.associations[vl_new_assoc].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(vl_new_assoc,c_timer_T1,v_assoc_db.associations[vl_new_assoc].assoc_data.rto)
+
+ // Send reply
+ p_sctp_req_api.reply(S_SCTP_Connect:{?} value {vl_new_assoc}) to vl_sender
+
+}
+
+// Send INIT
+function SCTP_message_send_init(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ var SCTP_Packet vl_sctp := c_empty_sctp_packet
+ var SCTP_Init_chunk vl_init_chunk := c_empty_init_chunk
+ vl_init_chunk:={
+ init_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tag,
+ a_rwnd:=65535,
+ os:=65535,
+ mis:=65535,
+ init_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn
+ }
+
+ // Indicate IDATA support if needed
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported or v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported){
+ var integer vl_idx:=0
+ if(ispresent(vl_init_chunk.params)){
+ vl_idx:=lengthof(vl_init_chunk.params)
+ }
+ vl_init_chunk.params[vl_idx].supported_extensions:={
+ param_type := 32776,
+ param_length :=0,
+ supported_extensions:={}
+ }
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
+ vl_init_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_init_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=64
+ }
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported){
+ vl_init_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_init_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=130
+ }
+ }
+
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported){
+ var integer vl_idx:=0
+ if(ispresent(vl_init_chunk.params)){
+ vl_idx:=lengthof(vl_init_chunk.params)
+ }
+ vl_init_chunk.params[vl_idx].forward_tsn_supported:={
+ param_type := 49152,
+ param_length :=0
+ }
+ }
+
+ vl_sctp:={
+ common_header :={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port
+ },
+ chunks:={{init:=vl_init_chunk}}
+ }
+
+ // Send the packet
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp)
+}
+
+/*****************************************************************************/
+//
+// Timeout handlers functions
+//
+/*****************************************************************************/
+// Handles the T1_init timer timeout
+// The event is already removed from the event_db
+function SCTP_timeout_T1_init(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+
+ if( v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.max_init_retrans){
+ // We should retransmitt the INIT
+ // increase the counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter +1
+
+ // Calculate the new T1_timer value
+ // See 6.3 of the RFC4960
+ var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
+ )
+ // Send the INIT
+ SCTP_message_send_init(pl_assoc_id)
+
+ // Start timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T1,vl_new_timeout)
+
+
+ } else {
+ // Give up
+
+ // Notify the user
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := true,
+ reason := "INIT timeout. No answer received from the remote side."
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ // Set the state of the association
+ SCTP_assoc_set_closed(pl_assoc_id)
+
+ }
+
+}
+
+
+
+// Handles the T1_cookie timer timeout
+// The event is already removed from the event_db
+function SCTP_timeout_T1_cookie(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+
+ if( v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.max_init_retrans){
+ // We should retransmitt the INIT
+ // increase the counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter +1
+
+ // Calculate the new T1_timer value
+ // See 6.3 of the RFC4960
+ var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
+ )
+ // Send the INIT
+ SCTP_message_send_cookie_echo(pl_assoc_id)
+
+ // Start timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T1_cookie,vl_new_timeout)
+
+
+ } else {
+ // Give up
+
+ // Notify the user
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := true,
+ reason := "Cookie echo timeout. No answer received from the remote side."
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(pl_assoc_id)
+
+ }
+
+}
+// Handles the T3_rtx timer timeout
+// The event is already removed from the event_db
+function SCTP_timeout_T3_rtx(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+ if( v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans){
+ // increase the counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter +1
+ // we shoudl mark the first chunk for retransmission
+ var integer vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
+ if(vl_i!=-1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=true
+ // try to be more agressive with resending. Resend teh half of the out queue
+ var integer vl_k:= (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries-1)/2
+ while(vl_k>0){
+ vl_i:= v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].next_element
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=true
+ vl_k:=vl_k-1
+ }
+ } else {
+ // Ops we forget to stop the timer
+ // nothing to do
+ return
+ }
+ // Adjust cwnd and ssthres
+ v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh:=SCTP_max(v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd/2,v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu*4)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:=v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu
+ v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes:=0
+
+ // Schedule the sending of the data packets
+ SCTP_data_send_scheduler(pl_assoc_id)
+ } else {
+ // Give up
+
+ // Notify the user
+ var SCTP_Notification_data vl_notification:={
+ assoc_id := pl_assoc_id,
+ notification:= {comm_lost := {}},
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := false,
+ reason := "Retransmit timeout. No answer received from the remote side."
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(pl_assoc_id)
+ }
+}
+
+// Handles the chutdown timer timeout
+function SCTP_timeout_T2_shutdown(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+ if( v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans){
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter +1
+
+
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.state==SCTP_ASSOC_SHUTDOWN_SENT){
+ // Resend shutdown
+ SCTP_message_send_shutdown(pl_assoc_id)
+ } else {
+ // Resend shutdown ack
+ SCTP_message_send_shutdown_ack(pl_assoc_id)
+ }
+
+ // calculate the new timeout
+ var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
+ )
+ // Schedule the resend
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T2_shutdown,vl_new_timeout)
+
+ } else {
+ // Give up
+
+ // Notify the user
+ var SCTP_Notification_data vl_notification:={
+ assoc_id := pl_assoc_id,
+ notification:= {comm_lost := {}},
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := false,
+ reason := "Retransmit timeout. No answer received from the remote side."
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(pl_assoc_id)
+ }
+
+}
+
+function SCTP_timeout_heartbeat(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id:=-1
+ if( v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter+1
+ SCTP_hearbeat_schedule(pl_assoc_id)
+
+ SCTP_message_send_heartbeat(pl_assoc_id)
+
+ } else {
+ // Give up
+
+ // Notify the user
+ var SCTP_Notification_data vl_notification:={
+ assoc_id := pl_assoc_id,
+ notification:= {comm_lost := {}},
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := false,
+ reason := "Heartbeat timeout. No answer received from the remote side."
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(pl_assoc_id)
+ }
+}
+
+function SCTP_timeout_close(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer:=-1
+ // Notify the user
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := true,
+ reason := "Shutdown finished"
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(pl_assoc_id)
+
+}
+
+function SCTP_timeout_reconfig(in integer pl_assoc_id) runs on SCTP_Engine_CT {
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=-1
+ if( v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter+1
+ var SCTP_Packet vl_sctp := c_empty_sctp_packet
+ vl_sctp:={
+ common_header:={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ },
+ chunks:={{re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req}}
+ }
+
+ // resend the last message
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
+ // restart the timer
+ SCTP_timer_reconf_start(pl_assoc_id)
+
+
+ } else {
+ // Give up
+
+ // Notify the user
+ var SCTP_Notification_data vl_notification:={
+ assoc_id := pl_assoc_id,
+ notification:= {comm_lost := {}},
+ options := omit
+ }
+ p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ var SCTP_Shutdown_data vl_sh_data:={
+ assoc_id:=pl_assoc_id,
+ graceful_shutdown := false,
+ reason := "Reconfig timeout. No answer received from the remote side."
+ }
+
+ p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
+
+ SCTP_assoc_set_closed(pl_assoc_id)
+ }
+}
+
+function SCTP_timeout_calculate_timeout_value(in float pl_rto, in integer pl_counter, in float pl_max) return float{
+ // Calculate the new timeout value
+ // See 6.3 of the RFC4960
+ var float vl_new_timeout:=pl_rto
+ for(var integer i:=1; i<=pl_counter; i:=i+1){
+ vl_new_timeout:=vl_new_timeout*2.0;
+ if(vl_new_timeout>=pl_max){
+ vl_new_timeout:=pl_max
+ break
+ }
+ }
+ return vl_new_timeout
+}
+
+/*****************************************************************************/
+//
+// Misc functions
+//
+/*****************************************************************************/
+
+// generate random a 32 bit non zero integer
+function SCTP_rnd_32bit_int() return integer {
+ return float2int(4294967295.0*rnd()) + 1
+}
+
+function SCTP_min(in integer pl_a, in integer pl_b) return integer {
+ if(pl_a<pl_b){
+ return pl_a
+ }
+ return pl_b
+}
+function SCTP_max(in integer pl_a, in integer pl_b) return integer {
+ if(pl_a>pl_b){
+ return pl_a
+ }
+ return pl_b
+}
+
+//return the next TSN, wrap if needed
+function SCTP_next_tsn(in integer pl_tsn) return integer {
+ return (pl_tsn+1) mod 4294967296 // the return value is between 0 and (2^32)-1 wrapping around
+}
+
+//return the previousTSN, wrap if needed
+function SCTP_prev_tsn(in integer pl_tsn) return integer {
+ return (pl_tsn-1) mod 4294967296 // the return value is between 0 and (2^32)-1 wrapping around
+}
+
+// Compares 2 TSNs, return -1, 0, 1
+function SCTP_compare_tsn(in integer pl_tsn_a, in integer pl_tsn_b) return integer {
+// See RFC 1982, SERIAL_BITS = 32
+ if(pl_tsn_a == pl_tsn_b){
+ return 0 // Equal
+ }
+ if(( (pl_tsn_a < pl_tsn_b) and ((pl_tsn_b - pl_tsn_a) > 2147483648)) or
+ ( (pl_tsn_a > pl_tsn_b) and ((pl_tsn_a - pl_tsn_b) < 2147483648)) ){
+ return 1 // pl_tsn_a Greater than pl_tsn_b
+ }
+
+ return -1 // pl_tsn_a not equal and not greater than pl_tsn_b
+
+}
+
+//return the next SSN, wrap if needed
+function SCTP_next_ssn(in integer pl_ssn) return integer {
+ return (pl_ssn+1) mod 65536 // the return value is between 0 and (2^16)-1 wrapping around
+}
+
+// Compares 2 SSNs, return -1, 0, 1
+function SCTP_compare_ssn(in integer pl_ssn_a, in integer pl_ssn_b) return integer {
+// See RFC 1982, SERIAL_BITS = 16
+ if(pl_ssn_a == pl_ssn_b){
+ return 0 // Equal
+ }
+ if(( (pl_ssn_a < pl_ssn_b) and ((pl_ssn_b - pl_ssn_a) < 32768)) or
+ ( (pl_ssn_a > pl_ssn_b) and ((pl_ssn_a - pl_ssn_b) > 32768)) ){
+ return 1 // pl_ssn_a Greater than pl_ssn_b
+ }
+
+ return -1 // pl_ssn_a not equal and not greater than pl_ssn_b
+
+}
+
+// Encode decode float
+external function SCTP_misc_float2oct(in float pl_in) return octetstring
+external function SCTP_misc_oct2float(in octetstring pl_in) return float
+
+/*****************************************************************************/
+//
+// int2int map handling functions
+//
+/*****************************************************************************/
+
+// Allocate a new int2int map
+external function SCTP_int2int_new() return integer
+
+// Free the int2int map
+external function SCTP_int2int_delete(in integer pl_map_id)
+
+// Add/Update value in a map
+external function SCTP_int2int_add(in integer pl_map_id, // The id of the map
+ in integer pl_key, // The key
+ in integer pl_value // the value stored for the key
+ ) return integer // 0 - OK, -1 - Error, wrong map id
+
+// Get value from a map
+external function SCTP_int2int_get(in integer pl_map_id, // The id of the map
+ in integer pl_key, // The key
+ out integer pl_value // the value stored for the key
+ ) return integer // 0 - OK, -1 - Error, wrong map id, wrong key
+
+
+/*****************************************************************************/
+//
+// Chunk queue handling functions
+//
+/*****************************************************************************/
+
+// Put an element into the end of the queue
+function SCTP_Chunk_queue_push(inout SCTP_Chunk_queue_db_t pl_chunk_db, in SCTP_Chunk_queue_element_t pl_chunk){
+ var integer vl_idx:=-1
+ if(pl_chunk_db.first_free==-1){
+ // No free element in the list
+ // Alocate a new one
+ vl_idx:=lengthof(pl_chunk_db.chunk_queue)
+ } else {
+ vl_idx:=pl_chunk_db.first_free
+ // remove it from the free queue
+ pl_chunk_db.first_free:=pl_chunk_db.chunk_queue[vl_idx].next_element
+ if(pl_chunk_db.first_free==-1){
+ // That was the last entry on the free list
+ pl_chunk_db.last_free:=-1
+ }
+ }
+
+ // Set the new element
+ pl_chunk_db.chunk_queue[vl_idx]:=pl_chunk;
+
+ // Chain the entry into the tail to the list
+ pl_chunk_db.chunk_queue[vl_idx].next_element:=-1; // This will be the last entry
+ if(pl_chunk_db.last_element!=-1){
+ pl_chunk_db.chunk_queue[pl_chunk_db.last_element].next_element:=vl_idx
+ }
+ pl_chunk_db.last_element:=vl_idx
+
+ if(pl_chunk_db.first_element==-1){
+ // There was no first element.
+ pl_chunk_db.first_element:=vl_idx
+ }
+ pl_chunk_db.used_entries:=pl_chunk_db.used_entries+1;
+}
+
+// Pop the first element from the queue
+function SCTP_Chunk_queue_pop(inout SCTP_Chunk_queue_db_t pl_chunk_db){
+ if(pl_chunk_db.used_entries>0){
+ // there is something to pop
+
+ // remove from the used list
+ var integer vl_idx:=pl_chunk_db.first_element
+ pl_chunk_db.first_element:=pl_chunk_db.chunk_queue[vl_idx].next_element
+ if(pl_chunk_db.first_element==-1){
+ // That was the last entry on the list
+ pl_chunk_db.last_element:=-1
+ }
+ // Clear the element
+ pl_chunk_db.chunk_queue[vl_idx]:=c_empty_Chunk_queue_element;
+
+
+ // put it into the end of the free list
+ pl_chunk_db.chunk_queue[vl_idx].next_element:=-1; // This will be the last entry
+ if(pl_chunk_db.last_free!=-1){
+ pl_chunk_db.chunk_queue[pl_chunk_db.last_free].next_element:=vl_idx
+ }
+ pl_chunk_db.last_free:=vl_idx
+ if(pl_chunk_db.first_free==-1){
+ // There was no first element.
+ pl_chunk_db.first_free:=vl_idx
+ }
+ pl_chunk_db.used_entries:=pl_chunk_db.used_entries-1;
+
+
+ }
+}
+
+
+// Examines the tx chunk queue and marks the packet, which should be abandoned.
+// If there are packets to abandon generates the forward TSN chunk
+function SCTP_data_abandon_marker(in integer pl_assoc_id, inout SCTP_Chunk_List pl_chunks_before_data) runs on SCTP_Engine_CT {
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported){
+ // the function is not active
+ return
+ }
+
+ var boolean vl_abandoned:= false
+ var integer vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
+
+ var integer_list vl_list:={}
+ while(vl_i!=-1){
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag){
+ // The chunk is marked for retransmission
+ // We should inspect only these chunks
+ if( (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].counter == 0) // reached the retransmit counter limit
+ or ( (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].lifetime>0.0) // reached the lifetime
+ and (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].lifetime<t_run_time.read)
+ )
+ ){
+ // abandon the packet
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=false
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].unmark_flag:= true
+ vl_list[sizeof(vl_list)]:=vl_i
+ vl_abandoned:=true
+ }
+
+ }
+ }
+
+ // If new packet were market to abandon
+ // find and mark all of the chunk & messages belongs to the marked chunks
+ if(vl_abandoned){
+ for(var integer vl_k:=0;vl_k<sizeof(vl_list);vl_k:=vl_k+1){
+ var integer vl_stream_id:=SCTP_data_get_chunk_stream_id(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_list[vl_k]].chunk)
+ var integer vl_ssn:=SCTP_data_get_chunk_ssn(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_list[vl_k]].chunk)
+
+ // mark the message as abandoned in the stream queue
+ SCTP_data_abandon_msg_in_stream(vl_stream_id,vl_ssn,SCTP_message_get_U_bit(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_list[vl_k]].chunk),pl_assoc_id)
+
+ // find the releated chunks in the chunk queue
+ vl_i:=SCTP_data_find_related(vl_stream_id,vl_ssn,vl_list[vl_k],v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue)
+ while(vl_i!=-1){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=false
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].unmark_flag:= true
+ vl_i:=SCTP_data_find_related(vl_stream_id,vl_ssn,vl_i,v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue)
+ }
+
+ }
+ }
+
+ // Generate the forward tsn chunk if needed
+ // check the beggining of the list.
+ var SCTP_Chunk vl_chunk
+
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
+ vl_chunk.iforward_tsn:={
+ chunk_type:=194,
+ flags:='00000000'B,
+ chunk_length:=0,
+ new_tsn:=-1,
+ stream_ssn_list:={}
+ }
+ } else {
+ vl_chunk.forward_tsn:={
+ chunk_type:=192,
+ flags:='00000000'B,
+ chunk_length:=0,
+ new_tsn:=-1,
+ stream_ssn_list:={}
+ }
+ }
+
+ vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
+
+ var boolean vl_iforward:=false
+ // We iterate through the chunks in order
+ while( (vl_i != -1) and (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].unmark_flag) ){
+ vl_iforward:=false
+ // set the tsn
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
+ vl_chunk.iforward_tsn.new_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].tsn
+ } else {
+ vl_chunk.forward_tsn.new_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].tsn
+ }
+
+ // add the skiped ssn to the map if needed
+ if(SCTP_message_get_U_bit(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk)=='0'B){
+ // ordered chunk
+ // find the index of the stream in the list
+ var integer vl_a:=0
+ var integer vl_len
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
+ vl_len:=lengthof(vl_chunk.iforward_tsn.stream_ssn_list)
+ } else {
+ vl_len:=lengthof(vl_chunk.forward_tsn.stream_ssn_list)
+ }
+
+ for(vl_a:=0;vl_a<vl_len;vl_a:=vl_a+1){
+ var integer vl_s
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
+ vl_s:=vl_chunk.iforward_tsn.stream_ssn_list[vl_a].stream_id
+ } else {
+ vl_s:=vl_chunk.forward_tsn.stream_ssn_list[vl_a].stream_id
+ }
+ if(vl_s==SCTP_data_get_chunk_stream_id(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk)){
+ break
+ }
+ }
+
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
+ vl_chunk.iforward_tsn.stream_ssn_list[vl_a]:={stream_id:=SCTP_data_get_chunk_stream_id(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk),
+ reserved:=0,
+ mid:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk.idata.mid
+ }
+ } else {
+ vl_chunk.forward_tsn.stream_ssn_list[vl_a]:={stream_id:=SCTP_data_get_chunk_stream_id(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk),
+ ssn:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk.data.ssn
+ }
+ }
+
+ }
+
+ SCTP_Chunk_queue_pop(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue)
+
+ // reset the T3 timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
+ SCTP_timer_T3_restart(pl_assoc_id)
+
+ vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
+
+ }
+
+ // Add forward tsn to the list if built
+ if(vl_iforward){
+ pl_chunks_before_data[lengthof(pl_chunks_before_data)]:=vl_chunk
+ }
+
+}
+
+
+// Finf the next chunk which belongs to the given stream-id, ssn, starting the search from the given idx
+function SCTP_data_find_related ( in integer pl_stream_id,
+ in integer pl_ssn,
+ in integer pl_idx,
+ in SCTP_Chunk_queue_element_list_t pl_chunk_queue
+ ) runs on SCTP_Engine_CT return integer{
+ if(SCTP_message_get_E_bit(pl_chunk_queue[pl_idx].chunk) == '1'B){
+ return -1 // This is the last chunk of the message, there is no next message
+ }
+
+ var bitstring vl_u:=SCTP_message_get_U_bit(pl_chunk_queue[pl_idx].chunk)
+ pl_idx:=pl_chunk_queue[pl_idx].next_element
+ while(pl_idx!=-1){
+ if( (pl_ssn==SCTP_data_get_chunk_ssn(pl_chunk_queue[pl_idx].chunk))
+ and (pl_stream_id==SCTP_data_get_chunk_stream_id(pl_chunk_queue[pl_idx].chunk))
+ and (vl_u==SCTP_message_get_U_bit(pl_chunk_queue[pl_idx].chunk))
+ ){
+ return pl_idx // found it
+ }
+ }
+
+ return -1
+}
+
+// Marks one message as abandoned in a stream queue
+function SCTP_data_abandon_msg_in_stream( in integer pl_stream_id,
+ in integer pl_ssn,
+ in bitstring pl_u_bit,
+ in integer pl_assoc_id
+ ) runs on SCTP_Engine_CT {
+ // find the idx of the stream
+ var integer vl_stream_idx:=-1
+ if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_stream_idx_map_id,
+ pl_stream_id,
+ vl_stream_idx
+ )!=-1){
+
+ if(pl_u_bit=='1'B){
+ vl_stream_idx:=vl_stream_idx+1 // we need to inspect the unordered queue
+ }
+ var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_stream_idx].messages.first_element
+ while(vl_idx!=-1){
+ var integer vl_comp:=SCTP_compare_ssn(pl_ssn, SCTP_data_get_chunk_ssn(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_stream_idx].messages.chunk_queue[vl_idx].chunk))
+ if(vl_comp==0){
+ // we found, the message
+ // mark it
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_stream_idx].messages.chunk_queue[vl_idx].unmark_flag:=true
+ return // nothing to do more
+ } else if(vl_comp<0){
+ // The ssn of the message is greater than the searhed one, nothing to do more
+ return
+ }
+ vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_stream_idx].messages.chunk_queue[vl_idx].next_element
+ }
+ }
+
+}
+
+// Assembles and send data packet
+// Tries to send as many as enabled by the congestion controll mechanism
+function SCTP_data_send_scheduler(in integer pl_assoc_id, in SCTP_Chunk_List pl_chunks_before_data:={}) runs on SCTP_Engine_CT {
+
+ var SCTP_Packet vl_sctp := c_empty_sctp_packet
+ vl_sctp:={
+ common_header :={
+ source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
+ destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
+ verification_tag := v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
+ },
+ chunks:=pl_chunks_before_data
+ }
+
+ SCTP_data_abandon_marker(pl_assoc_id,vl_sctp.chunks)
+
+ var integer vl_before_list_size:=lengthof(vl_sctp.chunks)
+//log("Schedule 1 ", v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd)
+//log("Schedule 2 ", v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh)
+//log("Schedule 3 ", v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes)
+ // If there is data chank marked for retransmission, we try to send them first regardless the cwnd
+ while(SCTP_data_add_packet(vl_sctp,pl_assoc_id)){
+//log("Send")
+ SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
+ vl_before_list_size := 0
+ vl_sctp.chunks:={}
+ }
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries!=0){
+ SCTP_timer_T3_start(pl_assoc_id) // start the timer if data chunk is on the fly
+ }
+//log("Schedule end ", v_assoc_db.associations[pl_assoc_id].assoc_data)
+
+ // If shuting down
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.state == SCTP_ASSOC_SHUTDOWN_RECEIVED){
+ if(SCTP_tx_queue_empty(pl_assoc_id)){ // No more data to send
+ // Send shutdown ack
+
+ // Stop all timers
+ SCTP_timer_stop_all(pl_assoc_id)
+
+ // Reset the retransmit counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
+
+ // Send shutdown ack
+ SCTP_message_send_shutdown_ack(pl_assoc_id)
+
+ // Start timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T2_shutdown,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
+
+ // Set the state
+ v_assoc_db.associations[pl_assoc_id].assoc_data.state := SCTP_ASSOC_SHUTDOWN_ACK_SENT
+
+ SCTP_hearbeat_stop(pl_assoc_id)
+ }
+ } else if(v_assoc_db.associations[pl_assoc_id].assoc_data.state == SCTP_ASSOC_SHUTDOWN_PENDING){
+ if(SCTP_tx_queue_empty(pl_assoc_id)){ // No more data to send
+ // Send shutdown
+
+ // Stop all timers
+ SCTP_timer_stop_all(pl_assoc_id)
+
+ // Reset the retransmit counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
+
+ // Send shutdown ack
+ SCTP_message_send_shutdown(pl_assoc_id)
+
+ // Start timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T2_shutdown,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
+
+ // Set the state
+ v_assoc_db.associations[pl_assoc_id].assoc_data.state := SCTP_ASSOC_SHUTDOWN_SENT
+
+ SCTP_hearbeat_stop(pl_assoc_id)
+ }
+ }
+
+}
+
+
+// Return true if all of the sending queues are empty
+function SCTP_tx_queue_empty(in integer pl_assoc_id) runs on SCTP_Engine_CT return boolean {
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries!=0){
+ return false // There are unacknowledged chunk
+ }
+ // check the stream queues
+ for(var integer vl_idx:=0;vl_idx<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues);vl_idx:=vl_idx+1){
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_idx].messages.used_entries!=0){
+ return false // There are data to send
+ }
+
+ }
+ return true
+}
+
+
+// Adds as many data packets to the message as many possible
+// Limits:
+// - pmtu
+// - congestion control
+// - Packet size
+function SCTP_data_add_packet(inout SCTP_Packet vl_sctp, in integer pl_assoc_id) runs on SCTP_Engine_CT return boolean {
+
+ var integer vl_packet_size:= 12 // the size of the common header
+ if(lengthof(vl_sctp.chunks)!=0){
+ // Calculate the actual packet size
+ var octetstring vl_tmp:=f_SCTP_enc(vl_sctp);
+ vl_packet_size:=lengthof(vl_tmp);
+ }
+
+ // We are allowed to send that many bytes now
+ var integer max_to_send:=v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd-v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size;
+ // calculate the maximum packet size
+ var integer rem_packet_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu-vl_packet_size;
+
+ // look for packet to retransmit
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries>0){
+ // the tx chunk queue is not empty
+ var integer vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
+ while(vl_i!=-1){
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag){
+ // the chunk is marked for retransmit
+ var integer vl_chunk_size:=SCTP_data_get_chunk_size(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk)
+
+ if(rem_packet_size>=vl_chunk_size){
+ // add the chunk
+ vl_sctp.chunks[sizeof(vl_sctp.chunks)]:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk
+ rem_packet_size:=rem_packet_size-vl_chunk_size
+
+ // Adjust the retransmit counter of the packet
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].counter>0){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].counter-1
+ }
+
+ // clear mark
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=false
+ } else {
+ // chunk doesn't fit into the packet
+ return sizeof(vl_sctp.chunks)>0 // there is something to send
+ }
+ }
+ vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].next_element
+ }
+ }
+
+ // Adjust the max_to_send according to the peer receive window
+ if(max_to_send>v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd){
+ if((v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd==0) and // The remote side closed the window
+ (v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size == 0) // and not data in fligth
+ ){ // we can try to send 1 data packet. Zero window probe
+ max_to_send:= SCTP_min(max_to_send,v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu) // max one MTU is allowed, if the cwnd allows it.
+ } else {
+ max_to_send:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd // limit the max data
+ }
+ }
+
+ // new data sending allowed
+ // 6.1 rule B
+ var integer vl_i:=0
+ while((vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues)) and (max_to_send>c_min_chunk_size ) and (rem_packet_size>c_min_chunk_size )){
+ // iterate through the queues for message to send
+ var integer vl_msg_q_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.scheduled_queue
+
+ var boolean vl_can_try_next := true
+ // The size of the chunk is limited by the
+ // - rem_packet_size
+ // - v_assoc_db.associations[pl_assoc_id].assoc_data.data_chunk_max_size
+ var integer vl_max_chunk_size:=SCTP_min(rem_packet_size,v_assoc_db.associations[pl_assoc_id].assoc_data.data_chunk_max_size)
+
+ if((v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_msg_q_idx].messages.used_entries>0)
+ and (not v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_msg_q_idx].flag)){
+ var integer vl_chunk_size:=SCTP_data_add_one_chunk(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_msg_q_idx] , // The msg queue of the stream
+ v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue, // The tx chunk queue
+ vl_sctp, // The SCTP message, will be sent
+ pl_assoc_id, // The association
+ vl_max_chunk_size, // The max size of the chunk
+ vl_can_try_next // true - last chunk of the message is added, false otherwise
+ )
+ rem_packet_size:=rem_packet_size-vl_chunk_size
+ max_to_send:=max_to_send-vl_chunk_size
+ }
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd==0){ // We added the zero window probe
+ return sizeof(vl_sctp.chunks)>0 // Should not add more chunk
+ }
+ // next queue
+ if(vl_can_try_next){
+ v_assoc_db.associations[pl_assoc_id].assoc_data.scheduled_queue:= (vl_msg_q_idx+1) mod lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues)
+ vl_i:=vl_i+1
+ }
+ }
+
+ if(vl_i==lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues)){
+ // no more dtat to send
+ v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes:=0
+ }
+return sizeof(vl_sctp.chunks)>0
+}
+
+// adds 1 chunk to the SCTP msg for sending
+// return the size of the added chunk
+// or 0 if not chunk added
+// indicates if the last chunk of the message were added
+
+function SCTP_data_add_one_chunk( inout SCTP_msg_queue_t pl_stream_tx_queue, // The msg queue of the stream
+ inout SCTP_Chunk_queue_db_t pl_tx_chunk_queue, // The tx chunk queue
+ inout SCTP_Packet pl_sctp, // The SCTP message, will be sent
+ in integer pl_assoc_id, // The association
+ in integer pl_max_size, // The max size of the chunk
+ out boolean pl_last_chunk // true - last chunk of the message is added, false otherwise
+ ) runs on SCTP_Engine_CT return integer {
+
+ var integer vl_queue_entry:=pl_stream_tx_queue.messages.first_element
+ var integer vl_chunk_size:=SCTP_data_get_chunk_size(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+ var integer vl_chunk_header_size:=SCTP_data_get_chunk_header_size(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+
+ var integer ret_val:=0
+ pl_last_chunk := false
+
+ if(pl_max_size<c_min_chunk_size){
+ return ret_val // Can't add more chunk
+ }
+
+ if( ((pl_stream_tx_queue.num_of_queued_octets==0)
+ and (pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].lifetime>=0.0)
+ and (pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].lifetime<t_run_time.read)) // lifetime expired
+
+ or
+
+ (pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].unmark_flag) //marked for abandon
+
+
+ ){
+ // drop the message
+ SCTP_Chunk_queue_pop(pl_stream_tx_queue.messages)
+ pl_stream_tx_queue.num_of_queued_octets:=0
+ return ret_val // Can't add more chunk
+
+ }
+
+ if( (pl_stream_tx_queue.num_of_queued_octets==0) and
+ ( vl_chunk_size <= pl_max_size)
+ ){
+ // The whole message is fit into the chunk
+ // set counters
+ v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+vl_chunk_size
+
+ // set the tsn
+ var integer vl_act_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn
+ v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=SCTP_next_tsn(vl_act_tsn)
+ pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].tsn:=vl_act_tsn
+ SCTP_data_set_chunk_tsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk,vl_act_tsn)
+
+ // Whole message in one chunk, set B & E bits
+ SCTP_message_set_B_bit('1'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+ SCTP_message_set_E_bit('1'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+
+ // copy the packet to the tx_queue
+ SCTP_Chunk_queue_push(pl_tx_chunk_queue,
+ pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry]
+ )
+
+ // add the chunk to the message
+ pl_sctp.chunks[sizeof(pl_sctp.chunks)]:=pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk
+
+
+ // remove it from the stream queue
+ SCTP_Chunk_queue_pop(pl_stream_tx_queue.messages)
+
+ pl_last_chunk := true // Last chunk
+ ret_val := vl_chunk_size // report the chunk size
+ } else {
+ // The message doesn't fit into one chunk, or fragmented
+ if(pl_stream_tx_queue.num_of_queued_octets==0){
+ // It should be fragmented
+ // Generate the first fragment
+ // set the tsn
+ var integer vl_act_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn
+ v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=SCTP_next_tsn(vl_act_tsn)
+ pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].tsn:=vl_act_tsn
+ SCTP_data_set_chunk_tsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk,vl_act_tsn)
+
+ // set B & E bits
+ SCTP_message_set_B_bit('1'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+ SCTP_message_set_E_bit('0'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+
+ // copy the packet to the tx_queue
+ SCTP_Chunk_queue_push(pl_tx_chunk_queue,
+ pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry]
+ )
+
+ // Set the next Fragment Sequence Number if I-DATA
+ SCTP_set_next_fsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+
+ // set the data part of the packet
+ var integer vl_chunk_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.last_element
+
+ // The whole message was copied, remove the unnessary data. Note: octetstring uses copy-on-write
+ SCTP_message_substr_chunk_data(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_chunk_idx].chunk,0,pl_max_size-vl_chunk_header_size);
+
+ // add the chunk to the message
+ pl_sctp.chunks[sizeof(pl_sctp.chunks)]:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_chunk_idx].chunk
+
+ // set counters
+ v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+pl_max_size
+ pl_stream_tx_queue.num_of_queued_octets:=pl_max_size-vl_chunk_header_size
+
+ pl_last_chunk := false // not the last chunk
+ ret_val := pl_max_size // report th echunk size
+
+ } else {
+ // already fragmented, next fragment
+ // It should be fragmented
+ // set the tsn
+ var integer vl_act_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn
+ v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=SCTP_next_tsn(vl_act_tsn)
+ pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].tsn:=vl_act_tsn
+ SCTP_data_set_chunk_tsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk,vl_act_tsn)
+
+ // set B & E bits
+ SCTP_message_set_B_bit('0'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+
+ if((vl_chunk_size-pl_stream_tx_queue.num_of_queued_octets)<=pl_max_size){
+ // Last chunk of the message
+ SCTP_message_set_E_bit('1'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+ ret_val:=vl_chunk_size-pl_stream_tx_queue.num_of_queued_octets
+ pl_last_chunk := true // not the last chunk
+ } else {
+ // middle chunk
+ SCTP_message_set_E_bit('0'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+ ret_val:=pl_max_size
+ pl_last_chunk := false // not the last chunk
+ }
+
+ // copy the packet to the tx_queue
+ SCTP_Chunk_queue_push(pl_tx_chunk_queue,
+ pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry]
+ )
+
+ // Set the next Fragment Sequence Number if I-DATA
+ SCTP_set_next_fsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
+
+ // The whole message was copied, remove the unnessary data. Note: octetstring uses copy-on-write
+ var integer vl_chunk_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.last_element
+ SCTP_message_substr_chunk_data(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_chunk_idx].chunk,pl_stream_tx_queue.num_of_queued_octets,ret_val-vl_chunk_header_size);
+
+ // add the chunk to the message
+ pl_sctp.chunks[sizeof(pl_sctp.chunks)]:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_chunk_idx].chunk
+ // set counters
+ v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+ret_val
+
+ if(pl_last_chunk){
+
+ // remove it from the stream queue
+ SCTP_Chunk_queue_pop(pl_stream_tx_queue.messages)
+ pl_stream_tx_queue.num_of_queued_octets:=0
+ } else {
+ pl_stream_tx_queue.num_of_queued_octets:=pl_stream_tx_queue.num_of_queued_octets+ret_val-vl_chunk_header_size
+ // middle chunk, we used all of the spaces in the packet
+ }
+ }
+ }
+ return ret_val
+}
+
+
+// Set the next Fragment Sequence Number if I-DATA
+function SCTP_set_next_fsn(inout SCTP_Chunk pl_chunk){
+ if(ischosen(pl_chunk.idata)){
+ // I-DATA chunk
+ if(pl_chunk.idata.B_flag=='1'B){
+ // Beginning chunk, the next fsn will be 1
+ pl_chunk.idata.ppid_fsn:=1
+ } else {
+ pl_chunk.idata.ppid_fsn:=SCTP_next_tsn(pl_chunk.idata.ppid_fsn) // +1 mod 2^32 like TSN
+ }
+ }
+}
+
+function SCTP_message_substr_chunk_data(inout SCTP_Chunk pl_chunk, in integer pl_begin, in integer pl_length){
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ pl_chunk.data.user_data:=substr(pl_chunk.data.user_data,pl_begin,pl_length)
+ } else {
+ // I-DATA chunk
+ pl_chunk.idata.user_data:=substr(pl_chunk.idata.user_data,pl_begin,pl_length)
+
+ }
+}
+
+function SCTP_message_set_B_bit(in bitstring pl_B_bit, inout SCTP_Chunk pl_chunk){
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ pl_chunk.data.B_flag:=pl_B_bit
+ } else {
+ // I-DATA chunk
+ pl_chunk.idata.B_flag:=pl_B_bit
+
+ }
+}
+
+function SCTP_message_get_B_bit(in SCTP_Chunk pl_chunk) return bitstring {
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ return pl_chunk.data.B_flag
+ } else {
+ // I-DATA chunk
+ return pl_chunk.idata.B_flag
+
+ }
+ return '0'B
+}
+
+function SCTP_message_set_E_bit(in bitstring pl_E_bit, inout SCTP_Chunk pl_chunk){
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ pl_chunk.data.E_flag:=pl_E_bit
+ } else {
+ // I-DATA chunk
+ pl_chunk.idata.E_flag:=pl_E_bit
+
+ }
+}
+
+function SCTP_message_get_E_bit(in SCTP_Chunk pl_chunk) return bitstring {
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ return pl_chunk.data.E_flag
+ } else {
+ // I-DATA chunk
+ return pl_chunk.idata.E_flag
+
+ }
+ return '0'B
+}
+
+function SCTP_message_get_U_bit(in SCTP_Chunk pl_chunk) return bitstring {
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ return pl_chunk.data.U_flag
+ } else {
+ // I-DATA chunk
+ return pl_chunk.idata.U_flag
+
+ }
+ return '0'B
+}
+function SCTP_data_get_chunk_size(in SCTP_Chunk pl_chunk) return integer {
+ var integer vl_chunk_size:=0;
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ vl_chunk_size:=16+lengthof(pl_chunk.data.user_data)
+ } else {
+ // I-DATA chunk
+ vl_chunk_size:=20+lengthof(pl_chunk.idata.user_data)
+
+ }
+ return vl_chunk_size
+}
+function SCTP_data_get_chunk_header_size(in SCTP_Chunk pl_chunk) return integer {
+ var integer vl_chunk_size:=0;
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ vl_chunk_size:=16
+ } else {
+ // I-DATA chunk
+ vl_chunk_size:=20
+
+ }
+ return vl_chunk_size
+}
+
+function SCTP_data_set_chunk_tsn(inout SCTP_Chunk pl_chunk, in integer pl_tsn){
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ pl_chunk.data.tsn:=pl_tsn
+ } else {
+ // I-DATA chunk
+ pl_chunk.idata.tsn:=pl_tsn
+ }
+
+}
+
+function SCTP_data_get_chunk_tsn(inout SCTP_Chunk pl_chunk) return integer{
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ return pl_chunk.data.tsn
+ } else {
+ // I-DATA chunk
+ return pl_chunk.idata.tsn
+ }
+ return -1
+}
+
+function SCTP_data_get_chunk_ssn(inout SCTP_Chunk pl_chunk) return integer{
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ return pl_chunk.data.ssn
+ } else {
+ // I-DATA chunk
+ return pl_chunk.idata.mid mod 65536
+ }
+ return -1
+}
+
+function SCTP_data_get_chunk_stream_id(inout SCTP_Chunk pl_chunk) return integer{
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ return pl_chunk.data.stream_id
+ } else {
+ // I-DATA chunk
+ return pl_chunk.idata.stream_id
+ }
+ return -1
+}
+
+// returns the sequence number used for reassembly
+// It is TSN for normal data packet
+// It is the FSN for I-data packet
+function SCTP_data_get_chunk_reassembly_seq_no(inout SCTP_Chunk pl_chunk) return integer{
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ return pl_chunk.data.tsn
+ } else {
+ // I-DATA chunk
+ if(pl_chunk.idata.B_flag == '0'B){
+ // Not the first chunk of the message
+ return pl_chunk.idata.ppid_fsn
+ } else {
+ // First chunk of the message, the FSN is 0
+ return 0
+ }
+ }
+ return -1
+}
+
+function SCTP_data_chunk_append_data(inout SCTP_Chunk pl_chunk_a, in SCTP_Chunk pl_chunk_b){
+ if(ischosen(pl_chunk_a.data)){
+ pl_chunk_a.data.user_data:=pl_chunk_a.data.user_data & pl_chunk_b.data.user_data
+ // Data chunk
+ } else {
+ // I-DATA chunk
+ pl_chunk_a.idata.user_data:=pl_chunk_a.idata.user_data & pl_chunk_b.idata.user_data
+ }
+}
+
+function SCTP_data_chunk_copy_tsn(inout SCTP_Chunk pl_chunk_a, in SCTP_Chunk pl_chunk_b){
+ if(ischosen(pl_chunk_a.data)){
+ pl_chunk_a.data.tsn:=pl_chunk_b.data.tsn
+ // Data chunk
+ } else {
+ // I-DATA chunk
+ pl_chunk_a.idata.tsn:=pl_chunk_b.idata.tsn
+ }
+}
+
+function SCTP_data_get_msg_data(in SCTP_Chunk pl_chunk,inout SCTP_MSG_data pl_msg_data, in integer pl_assoc_id) {// Copy the msg
+ if(ischosen(pl_chunk.data)){
+ // Data chunk
+ pl_msg_data:={
+ assoc_id:=pl_assoc_id,
+ ppid:=pl_chunk.data.ppid,
+ stream_id:=pl_chunk.data.stream_id,
+ data:=pl_chunk.data.user_data,
+ options:=omit
+ }
+ } else {
+ // I-DATA chunk
+ pl_msg_data:={
+ assoc_id:=pl_assoc_id,
+ ppid:=pl_chunk.idata.ppid_fsn,
+ stream_id:=pl_chunk.idata.stream_id,
+ data:=pl_chunk.idata.user_data,
+ options:=omit
+ }
+ }
+
+}
+
+// Start reconf timer if needed
+function SCTP_timer_reconf_start(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id!=-1){
+ // the timer is already running.
+ // Nothing to do
+ return
+ }
+ // Calculate the timeout
+ var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
+ )
+
+ //start the timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_reconfig,vl_new_timeout)
+
+
+}
+function SCTP_timer_reconf_stop(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=-1
+ }
+}
+
+
+//Start the T3-rtx timer if needed
+function SCTP_timer_T3_start(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
+ // the timer is already running.
+ // Nothing to do
+ return
+ }
+ // Calculate the timeout
+ var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
+ )
+
+ //start the timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T3_rtx,vl_new_timeout)
+
+
+}
+
+// Reschedule the T3-rtx timer
+function SCTP_timer_T3_restart(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ // Stop th etimer if running
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+ }
+
+ // Start it if there is not acked data
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries>0){
+ // Calculate the timeout
+ var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
+ )
+
+ //start the timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T3_rtx,vl_new_timeout)
+
+ }
+}
+
+function SCTP_timer_T2_restart(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ // Stop the timer if running
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
+ }
+ // Clear counter
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
+
+ //start the timer
+ v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
+ SCTP_event_schedule(pl_assoc_id,c_timer_T2_shutdown,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
+
+}
+// Initializes the congestion control parameters
+function SCTP_congestion_set_init_params(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ // initial cwn = min(4* pmtu, max(2*PMTU,4380)
+ var integer vl_cwnd:=4380
+ var integer vl_pmtu:=v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu
+ if((2*vl_pmtu)>vl_cwnd) {
+ vl_cwnd:=2*vl_pmtu
+ }
+ if((4*vl_pmtu)<vl_cwnd){
+ vl_cwnd:=4*vl_pmtu
+ }
+
+ v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:=vl_cwnd
+ v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd
+
+}
+
+// Start the heartbeat service
+function SCTP_hearbeat_start(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=0
+ SCTP_hearbeat_schedule(pl_assoc_id)
+}
+
+// Stop the heartbeat service
+function SCTP_hearbeat_stop(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id)
+ }
+ v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=0
+}
+
+// Schedule the next heartbeat
+function SCTP_hearbeat_schedule(in integer pl_assoc_id) runs on SCTP_Engine_CT{
+ if(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id!=-1){
+ SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id)
+ }
+ var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter,
+ v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
+ ) + v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.hb_interval
+ v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id:=SCTP_event_schedule(pl_assoc_id,c_timer_heartbeat,vl_new_timeout)
+}
+
+
+}
diff --git a/src/SCTP_Engine_PortTypes.ttcn b/src/SCTP_Engine_PortTypes.ttcn
new file mode 100644
index 0000000..e589b64
--- /dev/null
+++ b/src/SCTP_Engine_PortTypes.ttcn
@@ -0,0 +1,299 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2018 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v2.0
+// which accompanies this distribution, and is available at
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+///////////////////////////////////////////////////////////////////////////////
+//
+// File: SCTP_Engine_PortTypes.ttcn
+// Description: Test port definition of the SCTP Engine
+// Rev: <RnXnn>
+// Prodnr: CNL 113 840
+//
+module SCTP_Engine_PortTypes {
+
+
+// Upper side test port. Provides the SCTP API
+
+// Signature definition of the procedure calls
+// Client -> SCTP Engine component
+
+// If the requested operation was executed successfully all of the
+// procedure returns SCTP_Engine_result
+// If something went wrong an execption will be raised.
+
+// The assoc-id of the listen port is returned.
+// A separate assoc_id will be created for the accepted connections
+signature S_SCTP_Listen(in SCTP_Listen_data pl_arg)
+ return SCTP_Engine_result
+ exception (SCTP_operation_execption)
+
+signature S_SCTP_Connect(in SCTP_Connect_data pl_arg)
+ return SCTP_Engine_result
+ exception (SCTP_operation_execption)
+
+
+// The shutdown procedure and the release of the assoc_id shoud be done as:
+//
+// User initiated shutdown is a 3 step procedure:
+// call S_SCTP_Shutdown
+// receive S_SCTP_Shutdown_ind
+// call S_SCTP_Shutdown_conf
+//
+// Remote side initiated shutdown
+// receive S_SCTP_Shutdown_ind
+// call S_SCTP_Shutdown_conf
+//
+// The assoc_id is released only after the call of the S_SCTP_Shutdown_conf
+
+signature S_SCTP_Shutdown(in SCTP_Shutdown_data pl_arg)
+ return SCTP_Engine_result
+ exception (SCTP_operation_execption)
+
+
+signature S_SCTP_Shutdown_conf(in integer assoc_id)
+ return SCTP_Engine_result
+ exception (SCTP_operation_execption)
+
+
+// Request the sending of user data
+
+signature S_SCTP_Send_req(in SCTP_MSG_data pl_arg)
+ return SCTP_Engine_result
+ exception (SCTP_operation_execption)
+
+
+// Set options
+signature S_SCTP_options(in SCTP_Options_data pl_arg)
+ return SCTP_Engine_result
+ exception (SCTP_operation_execption)
+
+// Reconfiguration request
+signature S_SCTP_reconfig(in SCTP_Reconfiguration_req pl_arg)
+ return SCTP_Engine_result
+ exception (SCTP_operation_execption)
+
+
+// SCTP engine -> Client
+
+// Shutdown indication
+signature S_SCTP_Shutdown_ind(in SCTP_Shutdown_data pl_arg)
+
+// Connected indication.
+// called by the SCTP engine, when accepting a new client connection
+signature S_SCTP_Connected_ind(in SCTP_Connected_data pl_arg)
+
+// Receive data indication
+signature S_SCTP_Received_ind(in SCTP_MSG_data pl_arg)
+
+// Notification indication
+signature S_SCTP_Notification_ind(in SCTP_Notification_data pl_arg)
+
+// API type definitions
+type record SCTP_Notification_data {
+ integer assoc_id,
+ SCTP_Notification_union notification,
+ SCTP_Engine_Option_list options optional
+}
+
+type record SCTP_Notification_Comm_up{
+}
+
+type record SCTP_Notification_Comm_lost{
+}
+
+type union SCTP_Notification_union{
+ SCTP_Notification_Comm_up comm_up,
+ SCTP_Notification_Comm_lost comm_lost,
+ SCTP_Reconfiguration reconfig
+}
+
+type enumerated SCTP_Reconfig_result { RECONFIG_OK, RECONFIG_FAIL, RECONFIG_DENY }
+
+type record SCTP_Reconfiguration { // RFC 6525
+ SCTP_Reconfig_result result,
+ SCTP_Reconfiguration_method method optional
+}
+
+type record SCTP_Reconfiguration_req {
+ integer assoc_id,
+ SCTP_Reconfiguration_method reconf_method
+}
+
+
+type union SCTP_Reconfiguration_method{ // RFC 6525
+ SCTP_Reconfig_Stream_Reset stream_reset,
+ SCTP_Reconfig_Assoc_Reset assoc_reset,
+ SCTP_Reconfig_Stream_Add add_stream
+}
+
+type record SCTP_Reconfig_Stream_Reset{
+ boolean incoming,
+ record of integer streams optional
+}
+
+type record SCTP_Reconfig_Assoc_Reset{
+}
+
+type record SCTP_Reconfig_Stream_Add{
+ boolean incoming,
+ integer new_streams
+}
+
+
+type record SCTP_Connected_data {
+ integer assoc_id,
+ integer local_sctp_port,
+ integer remote_sctp_port,
+ integer listen_assoc_id // assoc_id of the listen port
+ // Use it to direct the new assoc-id to the right place
+}
+
+
+type record SCTP_Options_data {
+ integer assoc_id,
+ SCTP_Engine_Option_list options
+}
+
+
+type record SCTP_MSG_data {
+ integer assoc_id,
+ integer ppid,
+ integer stream_id,
+ octetstring data,
+ SCTP_Engine_Option_list options optional
+}
+
+
+type record SCTP_Engine_result{
+ integer assoc_id
+}
+
+type enumerated SCTP_Engine_cause {
+ SCTP_ENG_WRONG_ASSOC_ID,
+ SCTP_ENG_PORT_ALREADY_USED,
+ SCTP_ENG_WRONG_PORT,
+ SCTP_ENG_WRONG_PARAMETERS,
+ SCTP_ENG_INTERNAL_ERROR,
+ SCTP_ENG_WRONG_ASSOC_STATE,
+ SCTP_ENG_RECONFIG_ONGOING
+
+ // More cause code will be added later
+ }
+
+type record SCTP_operation_execption{
+ SCTP_Engine_cause cause,
+ charstring cause_text
+}
+
+type record SCTP_Shutdown_data{
+ integer assoc_id,
+ boolean graceful_shutdown, // true - initiate gracefull shutdown, false - send abort
+ charstring reason optional // if present sent as abort reason
+}
+
+type record SCTP_Listen_data {
+ integer local_sctp_port,
+ integer transport_id,
+ SCTP_Engine_Option_list options optional
+}
+
+
+type record SCTP_Connect_data {
+ integer local_sctp_port,
+ integer remote_sctp_port,
+ integer transport_id,
+ SCTP_Engine_Option_list options optional
+}
+
+
+
+// SCTP option definitions
+type record of SCTP_Engine_Option SCTP_Engine_Option_list;
+
+type union SCTP_Engine_Option {
+ boolean i_data_supported, // If true:
+ // Supplied for connection setup: negotiate iData support
+
+ boolean unordered, // Indicates that the MSG is unordered
+
+ SCTP_Reconfig_config reconfig_params,
+
+ boolean forward_tsn_supported, // indicates the support of RFC3758
+
+ float lifetime, // The relative lifetime of the message as RFC3758
+
+ integer max_retransmit_counter // See RFC 7496
+
+ // More will be added later
+}
+
+type record SCTP_Reconfig_config{
+ boolean is_supported,
+ boolean stream_reset_allowed,
+ boolean assoc_reset_allowed,
+ boolean stream_add_allowed
+}
+
+// Why procedure based?
+// Because the each operation is a request to execute some operation:
+// S_SCTP_Listen: means please start to
+// listen for an incoming association request
+//
+// The result of the operation should be syncronized and paired with the
+// request. The result should be available immediately without delay
+//
+// The procedure based port are represents a client-server connections
+// That is why we need 2 port
+// SCTP_Engine_API_request_PT : The SCTP user call the operation of the
+// SCTP engine component
+// SCTP_Engine_API_indication_PT: The SCTP engine notifies the user via this
+//
+// Using only one test port can mix up the result of ongoing operation and
+// the incoming indication calls
+
+// The SCTP API test port
+type port SCTP_Engine_API_request_PT procedure {
+ inout S_SCTP_Listen, S_SCTP_Connect, S_SCTP_Shutdown,S_SCTP_Shutdown_conf
+ inout S_SCTP_Send_req, S_SCTP_options, S_SCTP_reconfig
+} with {
+ extension "internal"
+}
+
+type port SCTP_Engine_API_indication_PT procedure {
+ inout S_SCTP_Shutdown_ind, S_SCTP_Connected_ind, S_SCTP_Received_ind
+ inout S_SCTP_Notification_ind
+} with {
+ extension "internal"
+}
+
+
+
+//******************************************************************************
+// Lower side port. Excahnges the encoded SCTP packets
+
+// Used to represent a SCTP packet in encoded form
+type record SCTP_Engine_packet {
+ integer transport_id, // The identifier used to identify the transport connection
+ // The identifier is specified in a Listen/Connect operation
+ // The associations using the same transport id are distinguished by the SCTP port numbers
+ octetstring sctp_packet // The SCTP packet
+}
+
+type record SCTP_Addr_change_notification{
+ integer transport_id // The identifier used to identify the transport connection
+}
+
+// used only between components
+type port SCTP_Engine_packet_PT message {
+ inout SCTP_Engine_packet
+ inout SCTP_Addr_change_notification
+} with {
+ extension "internal"
+}
+
+
+
+
+}
diff --git a/src/SCTP_Engine_Templates.ttcn b/src/SCTP_Engine_Templates.ttcn
new file mode 100644
index 0000000..b746f5f
--- /dev/null
+++ b/src/SCTP_Engine_Templates.ttcn
@@ -0,0 +1,67 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2018 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v2.0
+// which accompanies this distribution, and is available at
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+///////////////////////////////////////////////////////////////////////////////
+//
+// File: SCTP_Engine_Templates.ttcn
+// Description: Template definition of the SCTP Engine
+// Rev: <RnXnn>
+// Prodnr: CNL 113 840
+//
+module SCTP_Engine_Templates{
+ import from SCTP_Types all;
+
+ const SCTP_Common_header c_empty_sctp_common_header:={
+ source_port := 0,
+ destination_port := 0,
+ verification_tag := 0,
+ checksum := '00000000'O
+ }
+
+ const SCTP_Packet c_empty_sctp_packet:={
+ common_header := c_empty_sctp_common_header,
+ chunks :={}
+ }
+
+ const SCTP_abort_chunk c_empty_abort_chunk := {
+ chunk_type := 6,
+ T_flag := '0'B,
+ flags := '0000000'B,
+ chunk_length := 0,
+ params := omit
+ }
+
+ const SCTP_ShutdownComplete_chunk c_empty_shutdown_complete_chunk := {
+ chunk_type := 14,
+ T_flag := '0'B,
+ flags := '0000000'B,
+ chunk_length := 0
+ }
+
+ const SCTP_Init_chunk c_empty_init_chunk :={
+ chunk_type := 1,
+ flags := '00000000'B,
+ chunk_length := 0,
+ init_tag := 0,
+ a_rwnd := 0,
+ os := 0,
+ mis := 0,
+ init_tsn := 0,
+ params := omit
+ }
+
+ const SCTP_InitAck_chunk c_empty_init_ack_chunk :={
+ chunk_type := 2,
+ flags := '00000000'B,
+ chunk_length := 0,
+ init_tag := 0,
+ a_rwnd := 0,
+ os := 0,
+ mis := 0,
+ init_tsn := 0,
+ params := omit
+ }
+}