blob: 9d0a06642d3a0964d44c66d947abe658c8e089ce [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2000-2019 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_str