blob: 27e39ce8257a7885e4736589b6d65d1ba03b7802 [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2000-2018 Ericsson Telecom AB
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v2.0
// which accompanies this distribution, and is available at
// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
///////////////////////////////////////////////////////////////////////////////
//
// File: SCTP_Engine_Functions.ttcn
// Description: Functions of the SCTP Engine
// Rev: <RnXnn>
// Prodnr: CNL 113 840
//
module SCTP_Engine_Functions{
import from SCTP_Engine_Definition all;
import from SCTP_Engine_PortTypes all;
import from SCTP_Types all
import from SCTP_Engine_Templates all
// Allocates an entry in the assoc DB, returns the index of the entry
function SCTP_allocate_assoc_entry() runs on SCTP_Engine_CT return integer {
var integer vl_selected_entry:=-1;
if(v_assoc_db.used_entries<sizeof(v_assoc_db.associations)){
// There are free entries in the list
// remove it from the free list
vl_selected_entry:=v_assoc_db.first_free
v_assoc_db.first_free:=v_assoc_db.associations[vl_selected_entry].next_free
if(v_assoc_db.first_free == -1) {
// the last free entry was selected
v_assoc_db.last_free :=-1
}
v_assoc_db.associations[vl_selected_entry].next_free:=-1
} else {
// No free entry, add one to the list
vl_selected_entry:=sizeof(v_assoc_db.associations)
v_assoc_db.associations[vl_selected_entry] := c_empty_SCTP_Assoc_entry
v_assoc_db.used_entries:=v_assoc_db.used_entries+1
}
// initialize the data
v_assoc_db.associations[vl_selected_entry].assoc_data:=c_empty_SCTP_Assoc_data
// allocate int2int maps
v_assoc_db.associations[vl_selected_entry].assoc_data.tx_stream_idx_map_id := SCTP_int2int_new()
v_assoc_db.associations[vl_selected_entry].assoc_data.rx_stream_idx_map_id := SCTP_int2int_new()
return vl_selected_entry
}
// Put the association into closed state
function SCTP_assoc_set_closed(in integer pl_assoc_id) runs on SCTP_Engine_CT {
// Stop all timers
SCTP_timer_stop_all(pl_assoc_id)
// Remove the association from the map
if( SCTP_del_id_map(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id ,
v_assoc_db.associations[pl_assoc_id].assoc_data.local_port ,
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port ) == -1) {
// Something went wrong
log("INTERNAL ERROR: SCTP_timeout_T1_init SCTP_del_id_map: the association is not found in the id map ",pl_assoc_id , " " , v_assoc_db.associations[pl_assoc_id])
}
// Set the state of the association
v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_CLOSED
}
// Constructs the assoc entry from Cookie echo message
//
// Return value:
// - -1 error
// - assoc_id
function SCTP_assoc_construct_from_cookie_echo(in SCTP_Packet pl_sctp_packet,
in integer pl_transport_id := -1,
in integer pl_parent_id := -1
) runs on SCTP_Engine_CT return integer{
// first check the cookie validity
var integer pl_check_res :=-1
if( ( lengthof(pl_sctp_packet.chunks) >0 ) and ischosen(pl_sctp_packet.chunks[0].cookie_echo)){
// Seems to be a cookie echo message
// Check the cookie
pl_check_res:=SCTP_check_state_cookie(pl_sctp_packet.chunks[0].cookie_echo.cookie,v_secret_key);
if(pl_check_res == -1 ){
// invalid MAC
return -1;
}
// The cookie is either valid, or expired.
// Construct the assoc db entry. The caller will check for expired state
} else {
// Not a valid cookie echo
return -1;
}
// Allocate the new assoc data entry
var integer vl_new_assoc:=SCTP_allocate_assoc_entry();
// Copy the data from the parent if exists
if( pl_parent_id != -1) {
v_assoc_db.associations[vl_new_assoc]:=v_assoc_db.associations[pl_parent_id]
}
v_assoc_db.associations[vl_new_assoc].assoc_data:={
transport_id:=pl_transport_id,
local_port:=pl_sctp_packet.common_header.destination_port,
remote_port:=pl_sctp_packet.common_header.source_port,
cookie_validity:=pl_check_res
}
// extract data from state cookie
SCTP_cookie_extract(vl_new_assoc,pl_sctp_packet.chunks[0].cookie_echo.cookie);
return vl_new_assoc;
}
// Deallocates an entry in the assoc DB.
function SCTP_free_assoc_entry(in integer pl_idx) runs on SCTP_Engine_CT {
if( (pl_idx<0) or (pl_idx>sizeof(v_assoc_db.associations))
or (not ispresent(v_assoc_db.associations[pl_idx].assoc_data)) ){
return // invalid index or item is already free, nothing to do
}
// delete the int2int maps
SCTP_int2int_delete(v_assoc_db.associations[pl_idx].assoc_data.tx_stream_idx_map_id)
SCTP_int2int_delete(v_assoc_db.associations[pl_idx].assoc_data.rx_stream_idx_map_id)
// Stop all timers
SCTP_timer_stop_all(pl_idx)
// release all the data
v_assoc_db.associations[pl_idx].assoc_data:=omit
// This entry will be the last free entry
v_assoc_db.associations[pl_idx].next_free := -1
// put the entry to the end of the chain
if(v_assoc_db.last_free!=-1){
// There was valid last free entry
v_assoc_db.associations[v_assoc_db.last_free].next_free:=pl_idx
}
// Move the last_free pointer
v_assoc_db.last_free:=pl_idx
v_assoc_db.used_entries:=v_assoc_db.used_entries-1
return // We're done
}
function SCTP_timer_stop_all(in integer pl_assoc_id) runs on SCTP_Engine_CT {
if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id)
v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id:=-1
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer)
v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer:=-1
}
}
// Function to handle assoc-id <-> (transport_id,local_port,remote_port) mapping
// Register entries into the map
// return value: 0 - OK, -1 - Error
external function SCTP_add_id_map(in integer pl_assoc_id,
in integer pl_transport_id,
in integer pl_local_port,
in integer pl_remote_port) return integer
// Delete entries from the map
// return value: 0 - OK, -1 - Error
external function SCTP_del_id_map(in integer pl_transport_id,
in integer pl_local_port,
in integer pl_remote_port) return integer
// Search in the map
// return value: assoc_id - OK, -1 - Error
external function SCTP_find_id_map(in integer pl_transport_id,
in integer pl_local_port,
in integer pl_remote_port) return integer
// State cookie handling functions
// The information should be stored in state cookie
// timestamp & lifespan
// Own & remote verification tag
// Own & remote tie tag
// remote initial TSN, our own is equal to verification tag
// output & max input stream of remote
// remote advertised receive window
// Those are needed to restore the association data
// Our state cookie structure:
// The order and size of the fields:
// unix_timestamp 4 octets
// pl_lifespan 2
// pl_own_init_tag 4
// pl_remote_init_tag 4
// pl_tie_tag 4
// pl_remote_tsn 4
// pl_remote_os 2
// pl_remote_mis 2
// pl_remote_a_rwnd 4
// flags 1
// MAC 17
// Summ:47
// generate the state cookie. The actual time is queried.
external function SCTP_gen_state_cookie(in integer pl_lifespan,
in integer pl_own_init_tag,
in integer pl_remote_init_tag,
in integer pl_tie_tag,
in integer pl_remote_tsn,
in integer pl_remote_os,
in integer pl_remote_mis,
in integer pl_remote_a_rwnd,
in boolean pl_idata_flag,
in boolean pl_reconf_flag,
in boolean pl_forward_tsn_flag,
in octetstring pl_key
) return octetstring
// Check the received state cookie validity
// return values:
// 0: OK
// -1: MAC failed/ invalid contents
// 1: expired
external function SCTP_check_state_cookie(in octetstring pl_cookie,
in octetstring pl_key) return integer
// extract the data from the cookie into the assoc db entry
function SCTP_cookie_extract(in integer pl_assoc_id,
in octetstring pl_cookie) runs on SCTP_Engine_CT {
v_assoc_db.associations[pl_assoc_id].assoc_data:={
own_tag:=oct2int(substr(pl_cookie,6,4)),
remote_tag:=oct2int(substr(pl_cookie,10,4)),
remote_tsn:=oct2int(substr(pl_cookie,18,4)),
own_tsn:=oct2int(substr(pl_cookie,6,4)),
remote_os:=oct2int(substr(pl_cookie,22,2)),
remote_mis := oct2int(substr(pl_cookie,24,2)),
remote_a_rwnd := oct2int(substr(pl_cookie,26,4)),
i_data_supported := substr(oct2bit(substr(pl_cookie,30,1)),0,1) == '1'B,
reconf_own_seq_num := oct2int(substr(pl_cookie,6,4)),
reconf_remote_seq_num := oct2int(substr(pl_cookie,18,4))
}
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported:= substr(oct2bit(substr(pl_cookie,30,1)),1,1) == '1'B
v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported:= substr(oct2bit(substr(pl_cookie,30,1)),2,1) == '1'B
}
/*****************************************************************************/
//
// Event scheduler functions
//
/*****************************************************************************/
// Add event to the event-time map
// return value: 0 - OK, -1 - Error
external function SCTP_event_add_map(in integer pl_event_id,
in float pl_event_time)
// Remove event from the map
// return value: 0 - OK, -1 - Error
external function SCTP_event_del_map(in integer pl_event_id,
in float pl_event_time)
// Get the first event idx
// return value: event_idx - OK, -1 - Error
external function SCTP_event_get_map() return integer
// Schedule the event
// returns the idx of the
function SCTP_event_schedule(in integer pl_assoc_id,
in integer pl_timer_id,
in float pl_timeout // relative timeout value
) runs on SCTP_Engine_CT return integer {
var float curr_time:=t_run_time.read
var integer vl_selected_entry:=-1;
// log(v_event_db)
if(v_event_db.used_entries<sizeof(v_event_db.events)){
// There are free entries in the list
// remove it from the free list
vl_selected_entry:=v_event_db.first_free
v_event_db.first_free:=v_event_db.events[vl_selected_entry].next_free
if(v_event_db.first_free == -1) {
// the last free entry was selected
v_event_db.last_free :=-1
}
v_event_db.events[vl_selected_entry].next_free:=-1
} else {
// No free entry, add one to the list
vl_selected_entry:=sizeof(v_event_db.events)
}
v_event_db.used_entries:=v_event_db.used_entries+1;
// initialize the data
v_event_db.events[vl_selected_entry] := {
next_free:=-1,
assoc_id:=pl_assoc_id,
timer_id:=pl_timer_id,
timeout_time:=curr_time+pl_timeout // absulute timeout val
}
// insert the event into the map
SCTP_event_add_map(vl_selected_entry,v_event_db.events[vl_selected_entry].timeout_time)
// rescedule the timeout timer
SCTP_event_recalculate_timer()
return vl_selected_entry
}
// Remove the event from the list
function SCTP_event_remove(in integer pl_idx) runs on SCTP_Engine_CT {
if( (pl_idx<0) or (pl_idx>sizeof(v_event_db.events))
or (v_event_db.events[pl_idx].assoc_id==-1) ){
return // invalid index or item is already free, nothing to do
}
// remove the event from the map & reschedule the timer
SCTP_event_del_map(pl_idx,v_event_db.events[pl_idx].timeout_time)
SCTP_event_recalculate_timer()
// Free the enry and move to the end of the free chain
v_event_db.events[pl_idx]:=c_empty_event_entry
// put the entry to the end of the chain
if(v_event_db.last_free!=-1){
// There was valid last free entry
v_event_db.events[v_event_db.last_free].next_free:=pl_idx
}
// Set the first free pointer if needed
if(v_event_db.first_free==-1){
// There was valid last free entry
v_event_db.first_free:=pl_idx
}
// Move the last_free pointer
v_event_db.last_free:=pl_idx
v_event_db.used_entries:=v_event_db.used_entries-1;
}
// Set the t_next_event to the next event. Reset the timer if needed
function SCTP_event_recalculate_timer() runs on SCTP_Engine_CT {
var integer first_event:=SCTP_event_get_map();
if(first_event==-1){
// No event in the queue
v_event_db.the_sceduled_event:=-1; // No scheduled event
if(t_next_event.running) {
t_next_event.stop // deactivate the timer
}
return // we're ready
}
if(first_event==v_event_db.the_sceduled_event){
// the first event is the already scheduled, nothing to do
return
}
// Reschedule the timer
if(t_next_event.running) {
t_next_event.stop // stop it first
}
var float curr_time:=t_run_time.read
// Calculate the timeout value
var float schedule_time:=v_event_db.events[first_event].timeout_time-curr_time
if(schedule_time<0.0){
// do not chedule in the past
schedule_time:=0.0
}
// remeber the event idx
v_event_db.the_sceduled_event:=first_event
// Start the timer
t_next_event.start(schedule_time)
return
}
/*****************************************************************************/
//
// Message handler functions
//
/*****************************************************************************/
// Process the incoming sctp packet
// The INIT & COOKIE ECHO processed by other function
function SCTP_message_incoming_handler(in integer pl_assoc_id,
in SCTP_Packet pl_sctp_packet,
in boolean pl_cookie_preprocessed := false // if true, the Cookie Echo was preprocessed, only Cookie Ack is needed
) runs on SCTP_Engine_CT {
// Check the verification tag
if(pl_sctp_packet.common_header.verification_tag!=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tag){
// Not for me
// Drop it
log("Verification tag mismatch. Packet dropped. ",pl_sctp_packet)
return;
}
// trigger the processing of the data queue
// If new chunk has been put into the incoming chunk queue, set it true to trigger the processing of the queue
var boolean vl_process_data:=false
// The answer packet
var SCTP_Packet vl_response := c_empty_sctp_packet
for(var integer vl_k:=0;vl_k<lengthof(pl_sctp_packet.chunks);vl_k:=vl_k+1){
// Process the chunks by calling the handler functions
// Each handler returns the processing result
// -1: Stop the further processing
// 0: Continue with the next chunk
// 1: Continue with the next chunk, postprocess the data queues
var integer vl_handler_ret:=-1
if(ischosen(pl_sctp_packet.chunks[vl_k].init_ack)){
vl_handler_ret:=SCTP_message_incoming_init_ack_handler(pl_sctp_packet.chunks[vl_k].init_ack,pl_assoc_id,vl_k)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].cookie_ack)){
vl_handler_ret:=SCTP_message_incoming_cookie_ack_handler(pl_sctp_packet.chunks[vl_k].cookie_ack,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].cookie_echo)){ //Process cookie echo
vl_handler_ret:=SCTP_message_incoming_cookie_echo_handler(pl_sctp_packet.chunks[vl_k].cookie_echo,pl_assoc_id,vl_response,pl_cookie_preprocessed)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].data) or ischosen(pl_sctp_packet.chunks[vl_k].idata)){ // process DATA
vl_handler_ret:=SCTP_message_incoming_data_handler(pl_sctp_packet.chunks[vl_k],pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].sack)){ // process SACK
vl_handler_ret:=SCTP_message_incoming_sack_handler(pl_sctp_packet.chunks[vl_k].sack,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].abort)){ // process ABORT
vl_handler_ret:=SCTP_message_incoming_abort_handler(pl_sctp_packet.chunks[vl_k].abort,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown)){ // process SHUTDOWN
vl_handler_ret:=SCTP_message_incoming_shutdown_handler(pl_sctp_packet.chunks[vl_k].shutdown,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown_ack)){ // process SHUTDOWN ACK
vl_handler_ret:=SCTP_message_incoming_shutdown_ack_handler(pl_sctp_packet.chunks[vl_k].shutdown_ack,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown_complete)){ // process SHUTDOWN COMPLETE
vl_handler_ret:=SCTP_message_incoming_shutdown_complete_handler(pl_sctp_packet.chunks[vl_k].shutdown_complete,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].heartbeat)){ // process HEARTBEAT
vl_handler_ret:=SCTP_message_incoming_heartbeat_handler(pl_sctp_packet.chunks[vl_k].heartbeat,pl_assoc_id,vl_response)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].heartbeat_ack)){ // process HEARTBEAT
vl_handler_ret:=SCTP_message_incoming_heartbeat_ack_handler(pl_sctp_packet.chunks[vl_k].heartbeat_ack,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].forward_tsn)){ // process FORWARD TSN
vl_handler_ret:=SCTP_message_incoming_forward_tsn_handler(pl_sctp_packet.chunks[vl_k].forward_tsn,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].iforward_tsn)){ // process FORWARD TSN
vl_handler_ret:=SCTP_message_incoming_iforward_tsn_handler(pl_sctp_packet.chunks[vl_k].iforward_tsn,pl_assoc_id)
} else if(ischosen(pl_sctp_packet.chunks[vl_k].re_config)){ // process FORWARD TSN
vl_handler_ret:=SCTP_message_incoming_re_config_handler(pl_sctp_packet.chunks[vl_k].re_config,vl_response,pl_assoc_id)
}
if(vl_handler_ret == -1 ){
return // -1: Stop the further processing
} else if(vl_handler_ret == 1 ){
vl_process_data:=true // 1: Continue with the next chunk, postprocess the data queues
}
} // for
if(vl_process_data){
// construct SACK
SCTP_data_gen_sack(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue, // The queue
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn, // The first expected tsn of the queue, if empty
vl_response.chunks[lengthof(vl_response.chunks)],
pl_assoc_id) // The chunk
// if "deferred reset processing" is ongoing and received the missing data chunks
if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset)
and (SCTP_compare_tsn(SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset.last_assigned_tsn),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0) ){
// then execute the reset
var integer vl_idx:=lengthof(vl_response.chunks)
vl_response.chunks[vl_idx]:={
re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={}
}
}
SCTP_reconf_process_ssn_reset(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset,vl_response.chunks[vl_idx].re_config,pl_assoc_id)
v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset:=omit
}
// process the chunk queue and reconstruct the messages
SCTP_data_chunk2msg(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues,pl_assoc_id)
// deliver the data to the user
SCTP_data_deliver(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues,pl_assoc_id)
// Release the ack-ed and processed message from the rx chunk queue
SCTP_data_clean_tx_chunk_queue(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue,pl_assoc_id)
if(v_assoc_db.associations[pl_assoc_id].assoc_data.state == SCTP_ASSOC_SHUTDOWN_SENT){
// We should send shutdown also
var integer vl_sack_idx:=lengthof(vl_response.chunks)-1 // the index of the sack chunk
var SCTP_Shutdown_chunk vl_sh:={
chunk_type:=7,
flags:='00000000'B,
chunk_length:=0,
cum_tsn_ack:=vl_response.chunks[vl_sack_idx].sack.cum_tsn_ack
}
if(ispresent(vl_response.chunks[vl_sack_idx].sack.gap_blocks) or ispresent(vl_response.chunks[vl_sack_idx].sack.dup_tsns)){
// SACK is needed, add the shutdown after the sack
vl_response.chunks[lengthof(vl_response.chunks)].shutdown:=vl_sh
} else {
// SACK is not needed, replace it
vl_response.chunks[vl_sack_idx].shutdown:=vl_sh
}
// Restart the T2 timer
SCTP_timer_T2_restart(pl_assoc_id)
}
}
// Send the answer if need
if(lengthof(vl_response.chunks)>0){
vl_response.common_header:={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
}
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_response);
}
}
// The return values of the chunk handlers:
// -1: Stop the further processing
// 0: Continue with the next chunk
// 1: Continue with the next chunk, postprocess the data queues
// Handles the reconfig chunk
function SCTP_message_incoming_re_config_handler(in SCTP_Re_Config_chunk pl_re_cfg, // The chunk to process
inout SCTP_Packet pl_response,
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
// validate the chunk
var boolean vl_sendresp:=false
var boolean vl_storeas_req:=false
var SCTP_Re_Config_chunk vl_resp:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={}
}
if( v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported // re config support
and ispresent(pl_re_cfg.params)
and (
(lengthof(pl_re_cfg.params)==1) // one or two param present
or (lengthof(pl_re_cfg.params)==2) )
){
// process the requests
for(var integer vl_i:=0;vl_i<lengthof(pl_re_cfg.params);vl_i:=vl_i+1){
if(ischosen(pl_re_cfg.params[vl_i].out_ssn_reset_req)){
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id!=-1){ // timer of outgoing request is running
if(pl_re_cfg.params[vl_i].out_ssn_reset_req.reconf_resp_seq == v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq){
// This is the acknowledgement of the sent incoming ssn reset request
// stop the timer
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id)
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=-1
// No need to notify the user here, because the notification will be sent below
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false // no outgoing reconfig request as it is acked
v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=omit // acked
v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=-1
}
}
// Check the seq no
var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].out_ssn_reset_req.reconf_req_seq
if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
// this is just a hack to handle the case with two requests
and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
){
// retransmission
// send the latest response again
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
return 1
}
if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
// not the expected seq no
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={{
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=5,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}}
}
}
return 0
}
// adjust the remote seq no
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset)){
// reset already running
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={{
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=4,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}}
}
}
return 0
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_reset_allowed){ // Is SSN reset allowed?
if(SCTP_compare_tsn(SCTP_next_tsn(pl_re_cfg.params[vl_i].out_ssn_reset_req.last_assigned_tsn),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)>0){
// The remote last assigned TSN is greater than the sack-ed
// Enter "deferred reset processing"
// store the packet for the later processing
v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset:=pl_re_cfg.params[vl_i].out_ssn_reset_req
// prepare response
vl_resp.params[lengthof(vl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=6,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
vl_sendresp:=true
} else {
SCTP_reconf_process_ssn_reset(pl_re_cfg.params[vl_i].out_ssn_reset_req,vl_resp,pl_assoc_id)
vl_sendresp:=true
}
} else {
// send back the denied
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={{
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=2,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}}
}
}
return 0
}
} else if(ischosen(pl_re_cfg.params[vl_i].in_ssn_reset_req)){
// check seq no
// Check the seq no
var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].in_ssn_reset_req.reconf_req_seq
if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
// this is just a hack to handle the case with two requests
and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
){
// retransmission
// send the latest response again
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
return 1
}
if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
// not the expected seq no
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={{
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=5,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}}
}
}
return 0
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing){
// request is sent but not completed
// just ignore it
// The remote side will retransmit later
} else {
// TODO: handle request overlap
// generate outgoing ssn reset request
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_reset_allowed){ // Is SSN reset allowed?
if(ispresent(pl_re_cfg.params[vl_i].in_ssn_reset_req.stream_numbers)){
SCTP_reconf_generate_out_ssn_reset_req(pl_re_cfg.params[vl_i].in_ssn_reset_req.stream_numbers,vl_in_seq_no,vl_resp,pl_assoc_id)
} else {
SCTP_reconf_generate_out_ssn_reset_req({},vl_in_seq_no,vl_resp,pl_assoc_id)
}
vl_sendresp:=true;
}else {
// send back the denied
vl_resp.params[lengthof(vl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=2,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
vl_sendresp:=true;
}
}
} else if(ischosen(pl_re_cfg.params[vl_i].add_out_stream)){
var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].add_out_stream.reconf_req_seq
if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
// this is just a hack to handle the case with two requests
and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
){
// retransmission
// send the latest response again
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
return 1
}
if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
// not the expected seq no
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={{
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=5,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}}
}
}
return 0
}
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_add_allowed){ // allowed?
// send back ok
vl_resp.params[lengthof(vl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=1,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
vl_sendresp:=true;
// notify the user
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_OK,
method:={add_stream:={
incoming:=false,
new_streams:=pl_re_cfg.params[vl_i].add_out_stream.stream_num
}
}
}
},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
} else {
// send back the denied
vl_resp.params[lengthof(vl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=2,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
vl_sendresp:=true;
}
} else if(ischosen(pl_re_cfg.params[vl_i].add_in_stream)){
var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].add_in_stream.reconf_req_seq
if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
// this is just a hack to handle the case with two requests
and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
){
// retransmission
// send the latest response again
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
return 1
}
if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
// not the expected seq no
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={{
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=5,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}}
}
}
return 0
}
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_add_allowed){ // Is allowed?
// send back ok
vl_resp.params[lengthof(vl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=1,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
vl_sendresp:=true;
// send the add outgoing stream req
var SCTP_Packet vl_out_req:=c_empty_sctp_packet
vl_out_req.common_header:={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
}
vl_out_req.chunks[0]:={ re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={{
add_out_stream:={
param_type:=17,
param_length:=0,
reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num,
stream_num:=pl_re_cfg.params[vl_i].add_in_stream.stream_num,
reserved:=0
}
}}
}
}
v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num:=SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num)
v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=vl_out_req.chunks[0].re_config
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0
// schedule the timer
SCTP_timer_reconf_start(pl_assoc_id)
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_out_req);
} else {
// send back the denied
vl_resp.params[lengthof(vl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=2,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
vl_sendresp:=true;
}
} else if(ischosen(pl_re_cfg.params[vl_i].ssn_tsn_reset_req)){
var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].ssn_tsn_reset_req.reconf_req_seq
if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no
or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no
// this is just a hack to handle the case with two requests
and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp)
){
// retransmission
// send the latest response again
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp}
return 1
}
if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){
// not the expected seq no
pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={{
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=5,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}}
}
}
return 0
}
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no)
// process the request
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.assoc_reset_allowed){ // Is reset allowed?
// Compute an appropriate value for the Receiver's Next TSN
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:= (v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn + 2147483648) mod 4294967296
// Compute an appropriate value for the local endpoint's next TSN
// v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn already contains that value
// Reset the stream and chunk queues
// clear the incoming message queues
for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue
}
// Clear incoming chunk queue
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue := c_empty_chunk_queue_db
// Clear outgoing chunk queue
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue := c_empty_chunk_queue_db
// clear the outgoing message queues
for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues);vl_k:=vl_k+1){
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_k]:=c_empty_msg_queue
}
// stop the T3 timer.
if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
}
vl_resp.params[lengthof(vl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=1,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
vl_sendresp:=true;
// notify the user
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_OK,
method:={assoc_reset:={}
}
}
},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
} else {
// send back the denied
vl_resp.params[lengthof(vl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=vl_in_seq_no,
result:=2,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
vl_sendresp:=true;
}
} else if(ischosen(pl_re_cfg.params[vl_i].reconf_resp)){
if(pl_re_cfg.params[vl_i].reconf_resp.reconf_resp_seq==v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq){
// we got a response for our request
// stop the timers
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false
SCTP_timer_reconf_stop(pl_assoc_id)
// get the request type
// check the answere code
if(pl_re_cfg.params[vl_i].reconf_resp.result==2){
// denied
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_DENY,
method:=omit
}
},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_reconf_clear_marks(pl_assoc_id)
} else if(pl_re_cfg.params[vl_i].reconf_resp.result==6) {
// In progress, restart the timer and clear the retransmission counter
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0
SCTP_timer_reconf_start(pl_assoc_id)
} else if(pl_re_cfg.params[vl_i].reconf_resp.result>2 and pl_re_cfg.params[vl_i].reconf_resp.result<6) {
// fail
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_FAIL,
method:=omit
}
},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_reconf_clear_marks(pl_assoc_id)
} else {
// success
// check the type of the request
var SCTP_parameters vl_req
if(SCTP_reconf_get_out_req( v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req,vl_req)){
if(ischosen(vl_req.add_out_stream)){
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_OK,
method:= {add_stream:={
incoming:= true,
new_streams:=vl_req.add_out_stream.stream_num
}
}
}
},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
} else if(ischosen(vl_req.out_ssn_reset_req)){
// reset the ssn's
// the queues are already marked
for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){
if(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k].flag){
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue
}
}
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_OK,
method:= {stream_reset:={
incoming:= true,
streams:=vl_req.out_ssn_reset_req.stream_numbers
}
}
}
},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
} else if(ischosen(vl_req.ssn_tsn_reset_req)){
// reset the assoc
// clear the incoming message queues
for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue
}
// Clear incoming chunk queue
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue := c_empty_chunk_queue_db
// Clear outgoing chunk queue
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue := c_empty_chunk_queue_db
// clear the outgoing message queues
for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues);vl_k:=vl_k+1){
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_k]:=c_empty_msg_queue
}
// stop the T3 timer.
if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
}
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:= pl_re_cfg.params[vl_i].reconf_resp.sender_next_tsn
v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=pl_re_cfg.params[vl_i].reconf_resp.receiver_next_tsn
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_OK,
method:= {assoc_reset:={
}
}
}},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
}
}
}
} else if(pl_re_cfg.params[vl_i].reconf_resp.reconf_resp_seq==v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq){
// we got a response for our request of do something with the incoming streams
// Just stop the timer as we should receive matching request
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false
SCTP_timer_reconf_stop(pl_assoc_id)
// check the answere code
if(pl_re_cfg.params[vl_i].reconf_resp.result==2){
// denied
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_DENY,
method:=omit
}
},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
} else if(pl_re_cfg.params[vl_i].reconf_resp.result>2) {
// fail
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_FAIL,
method:=omit
}
},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
}
}
// else do nothing
}
} // for
} else {
// protocol error
pl_response.chunks[lengthof(pl_response.chunks)]:={
error_:={
chunk_type := 9,
flags:= '00000000'B,
chunk_length:=0,
params:={
{protocol_violation:={cause_code:=13,cause_length:=0,reason:=''O}}
}
}
}
}
if(vl_sendresp){
pl_response.chunks[lengthof(pl_response.chunks)]:={re_config:=vl_resp}
v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp:=vl_resp
}
return 0
}
function SCTP_reconf_get_out_req(in SCTP_Re_Config_chunk pl_chunk, out SCTP_parameters pl_req) return boolean {
if(ispresent(pl_chunk.params)){
for(var integer vl_i; vl_i<lengthof(pl_chunk.params);vl_i:=vl_i+1){
if(ischosen(pl_chunk.params[vl_i].out_ssn_reset_req)
or ischosen(pl_chunk.params[vl_i].add_out_stream)
or ischosen(pl_chunk.params[vl_i].ssn_tsn_reset_req)){
pl_req:=pl_chunk.params[vl_i]
return true
}
}
}
return false
}
function SCTP_reconf_clear_marks(in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT{
for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i].flag:=false;
}
}
function SCTP_reconf_generate_out_ssn_reset_req( in integer_list pl_stream_list,
in integer pl_in_seq_no,
inout SCTP_Re_Config_chunk pl_resp,
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT{
var integer vl_idx:=lengthof(pl_resp.params)
pl_resp.params[vl_idx]:={out_ssn_reset_req:={
param_type:=13,
param_length:=0,
reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num,
reconf_resp_seq:=pl_in_seq_no,
last_assigned_tsn:=SCTP_prev_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn),
stream_numbers:=omit
}}
if(lengthof(pl_stream_list)>0){
pl_resp.params[vl_idx].out_ssn_reset_req.stream_numbers:=pl_stream_list
// mark the lists as closed
for(var integer vl_i:=0;vl_i<lengthof(pl_stream_list);vl_i:=vl_i+1){
var integer vl_stream_idx
if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
pl_stream_list[vl_i],
vl_stream_idx
)!=-1){
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx].flag:=true;
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx+1].flag:=true;
}
}
} else {
// mark all stream as closed
for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i].flag:=true;
}
}
v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num:=SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num)
v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=pl_resp
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0
// schedule the timer
SCTP_timer_reconf_start(pl_assoc_id)
}
function SCTP_reconf_process_ssn_reset(in SCTP_Out_SSN_Reset_req_parameter pl_re_cfg, // The chunk to process
inout SCTP_Re_Config_chunk pl_resp,
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT{
var SCTP_Notification_data vl_note_data:={
assoc_id:=pl_assoc_id,
notification := {
reconfig:={
result:=RECONFIG_OK,
method:={stream_reset:={
incoming:=false,
streams:={}
}
}
}
},
options := omit
}
if(ispresent(pl_re_cfg.stream_numbers)){
// reset only the specified streams
for(var integer vl_i:=0;vl_i<lengthof(pl_re_cfg.stream_numbers);vl_i:=vl_i+1){
var integer vl_stream_idx
vl_note_data.notification.reconfig.method.stream_reset.streams[lengthof(vl_note_data.notification.reconfig.method.stream_reset.streams)]:=pl_re_cfg.stream_numbers[vl_i]
if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
pl_re_cfg.stream_numbers[vl_i],
vl_stream_idx
)!=-1){
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx]:=c_empty_msg_queue
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx+1]:=c_empty_msg_queue
}
}
} else {
// reset all streams
for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i]:=c_empty_msg_queue
}
}
// generate the ack message
pl_resp.params[lengthof(pl_resp.params)]:={
reconf_resp:={
param_type:=16,
param_length:=0,
reconf_resp_seq:=pl_re_cfg.reconf_req_seq,
result:=1,
sender_next_tsn:=omit,
receiver_next_tsn:=omit
}
}
v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp:=pl_resp
// Notify the user
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
}
// process I-forward TSN chunk
function SCTP_message_incoming_iforward_tsn_handler(in SCTP_IForward_TSN_chunk pl_fwd_tsn, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
if(SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<0){
// The new TSN has been acked already. Just trigger the SACK
return 1
}
// Clean up the data queue
// Mark every hole as processed packet until the new tsn
// The data processor part will do the magic later
var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.first_element
while(vl_idx!=-1){
if( SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].tsn)<0){
// the tsn of the chunk is greater than the new tsn
break; // we are done
}
if(not ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].chunk)){
// A hole, mark it.
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].mark_flag:= true
}
vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].next_element
}
// clean up the queues
for(var integer vl_i:=0;vl_i<lengthof(pl_fwd_tsn.stream_ssn_list);vl_i:=vl_i+1){
SCTP_message_queue_skip_ssn(pl_assoc_id,pl_fwd_tsn.stream_ssn_list[vl_i].stream_id,pl_fwd_tsn.stream_ssn_list[vl_i].mid mod 65536,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues )
}
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(pl_fwd_tsn.new_tsn)
return 1 // trigger the data processing
}
// process forward TSN chunk
function SCTP_message_incoming_forward_tsn_handler(in SCTP_Forward_TSN_chunk pl_fwd_tsn, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
if(SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<0){
// The new TSN has been acked already. Just trigger the SACK
return 1
}
// Clean up the data queue
// Mark every hole as processed packet until the new tsn
// The data processor part will do the magic later
var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.first_element
while(vl_idx!=-1){
if( SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].tsn)<0){
// the tsn of the chunk is greater than the new tsn
break; // we are done
}
if(not ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].chunk)){
// A hole, mark it.
v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].mark_flag:= true
}
vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].next_element
}
// clean up the queues
for(var integer vl_i:=0;vl_i<lengthof(pl_fwd_tsn.stream_ssn_list);vl_i:=vl_i+1){
SCTP_message_queue_skip_ssn(pl_assoc_id,pl_fwd_tsn.stream_ssn_list[vl_i].stream_id,pl_fwd_tsn.stream_ssn_list[vl_i].ssn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues )
}
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(pl_fwd_tsn.new_tsn)
return 1 // trigger the data processing
}
// Mark the messages as skiped based in the forward tsn chunk data
function SCTP_message_queue_skip_ssn( in integer pl_assoc_id, // The association
in integer pl_stream_id,
in integer pl_skipped_ssn,
inout SCTP_msg_queue_list_t pl_msg_queue_list
) runs on SCTP_Engine_CT {
var integer vl_stream_idx:=-1; // index of the stream in the pl_msg_queue_list
if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
pl_stream_id,
vl_stream_idx
)==-1){
// not seen stream
// create it
vl_stream_idx:=lengthof(pl_msg_queue_list)
// ordered queue
pl_msg_queue_list[vl_stream_idx]:=c_empty_msg_queue;
pl_msg_queue_list[vl_stream_idx].next_ssn:=SCTP_next_ssn(pl_skipped_ssn); // store the next expected ssn
// undered queue
pl_msg_queue_list[vl_stream_idx+1]:=c_empty_msg_queue;
pl_msg_queue_list[vl_stream_idx+1].next_ssn:=-1; // Not used queue
} else {
var integer vl_idx:=pl_msg_queue_list[vl_stream_idx].messages.first_element
while(vl_idx!=-1){
if(SCTP_compare_ssn(SCTP_data_get_chunk_ssn(pl_msg_queue_list[vl_stream_idx].messages.chunk_queue[vl_idx].chunk),pl_skipped_ssn )<=0){
// ssn less than the last skiped, mark it
pl_msg_queue_list[vl_stream_idx].messages.chunk_queue[vl_idx].mark_flag:=true
} else {
break; //no more ssn to mark
}
}
if(SCTP_compare_ssn(pl_msg_queue_list[vl_stream_idx].next_ssn,pl_skipped_ssn)<=0){
// afdjust the next ssn if needed
pl_msg_queue_list[vl_stream_idx].next_ssn:=SCTP_next_ssn(pl_skipped_ssn)
}
}
}
// Handles the incoming HEARTBEAT_ACK
function SCTP_message_incoming_heartbeat_ack_handler(in SCTP_Heartbeat_ack_chunk pl_hb_ack, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
// reset the hearbeat counter
v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=0
return 0
}
// Handles the incoming HEARTBEAT
function SCTP_message_incoming_heartbeat_handler(in SCTP_Heartbeat_chunk pl_hb, // The chunk to process
in integer pl_assoc_id, // The association
inout SCTP_Packet pl_response // Add the heartbeat_ack to the response
) runs on SCTP_Engine_CT return integer {
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case ( (SCTP_ASSOC_COOKIE_ECHOED,SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_RECEIVED)){
// A receiver of a HEARTBEAT MUST respond to a
// HEARTBEAT with a HEARTBEAT-ACK after entering the COOKIE-ECHOED state
// (INIT sender) or the ESTABLISHED state (INIT receiver), up until
// reaching the SHUTDOWN-SENT state (SHUTDOWN sender) or the SHUTDOWN-
// ACK-SENT state (SHUTDOWN receiver).
// Add the heartbeat ack chunk to the response
pl_response.chunks[lengthof(pl_response.chunks)]:={heartbeat_ack:={ chunk_type :=5,
flags := '00000000'B,
chunk_length := 0,
params :=pl_hb.params
}}
}
}
return 0
}
// Handles the incoming SHUTDOWN COMPLETE
function SCTP_message_incoming_shutdown_complete_handler(in SCTP_ShutdownComplete_chunk pl_shutdown, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case ( (SCTP_ASSOC_SHUTDOWN_SENT,SCTP_ASSOC_SHUTDOWN_ACK_SENT)){
// Notify the user
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := true,
reason := "Shutdown finished"
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(pl_assoc_id)
}
}
return -1
}
// Handles the incoming SHUTDOWN ACK
function SCTP_message_incoming_shutdown_ack_handler(in SCTP_Shutdown_ack_chunk pl_shutdown, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case ( (SCTP_ASSOC_SHUTDOWN_SENT,SCTP_ASSOC_SHUTDOWN_ACK_SENT)){
SCTP_timer_stop_all(pl_assoc_id)
// Send Shutdown Complete
SCTP_message_send_shutdown_complete(-,-,pl_assoc_id)
// delay the shutdown ind in order to give a chance to deliver the shutdown complete message
// The user may destroy the underlying transport in the case of shutdown ind
v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_CLOSE_TIME_WAIT
v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer:=SCTP_event_schedule(pl_assoc_id,c_timer_close,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
}
}
return -1
}
// Handles the incoming SHUTDOWN
function SCTP_message_incoming_shutdown_handler(in SCTP_Shutdown_chunk pl_shutdown, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case ( (SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING)){
// Set the state
v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_SHUTDOWN_RECEIVED
// Process the cum_tsn_ack
SCTP_message_cumulative_ack_handler(pl_shutdown.cum_tsn_ack,pl_assoc_id)
// Schedule the sending of the data
// The data send scheduler will check the state and if no outstanding data
// will send shutdown ack and move to
SCTP_data_send_scheduler(pl_assoc_id)
// Notify the user
if(v_assoc_db.associations[pl_assoc_id].assoc_data.state==SCTP_ASSOC_ESTABLISHED){
var SCTP_Notification_data vl_notification:={
assoc_id := pl_assoc_id,
notification:= {comm_lost := {}},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
}
}
case (SCTP_ASSOC_SHUTDOWN_RECEIVED){
// Process the cum_tsn_ack
SCTP_message_cumulative_ack_handler(pl_shutdown.cum_tsn_ack,pl_assoc_id)
// Schedule the sending of the data
// The data send scheduler will check the state and if no outstanding data
// will send shutdown ack and move to
SCTP_data_send_scheduler(pl_assoc_id)
}
case (SCTP_ASSOC_SHUTDOWN_SENT){
// Set the state
v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_SHUTDOWN_RECEIVED
// The data send scheduler will check the state and if no outstanding data
// will send shutdown ack
SCTP_data_send_scheduler(pl_assoc_id)
}
}
return 0
}
// Handles the incoming ABORT
function SCTP_message_incoming_abort_handler(in SCTP_abort_chunk pl_abort, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
// Notify the user
var SCTP_Notification_data vl_notification:={
assoc_id := pl_assoc_id,
notification:= {comm_lost := {}},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := false,
reason := "Abort received"
}
// Store the reason if available
if(ispresent(pl_abort.params)){
vl_sh_data.reason:=log2str(pl_abort.params)
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(pl_assoc_id)
// Stop the further processing
return -1
}
// Handles the incoming SACK
function SCTP_message_incoming_sack_handler(in SCTP_SAck_chunk pl_sack, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case ((SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_RECEIVED)){
var integer vl_tsn_comp:=SCTP_message_cumulative_ack_handler( pl_sack.cum_tsn_ack,pl_assoc_id)
if(vl_tsn_comp==-1){
// TSN already acked
// do nothing
return 0 // next chunk
}
// update the peers window size
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd:= pl_sack.a_rwnd
if(vl_tsn_comp==0){
// No new TSN are ACK-ed.
if(( pl_sack.a_rwnd==0) and // the peers window is closed
(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter == v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans) // retransmitting the zero probe, last trial
){
// Don't let the retransmitt counter overflow
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter := v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter - 1
}
} else {
// stop the T3 timer, the SCTP_data_send_scheduler will restart it if needed
if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
}
// Something ack-ed -> try to send the remaining data
SCTP_data_send_scheduler(pl_assoc_id)
}
// TODO: handle gaps for fast retransmit
}
}
return 0
}
// Processes the cumulative TSN ack
// return value:
// -1: The acked TSN is less than the last acked
// 0: The acked TSN is the same as the last acked
// 1: New TSN is acked
function SCTP_message_cumulative_ack_handler(in integer pl_ack, in integer pl_assoc_id) runs on SCTP_Engine_CT return integer {
var integer vl_tsn_comp:=SCTP_compare_tsn( pl_ack,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn)
if(vl_tsn_comp==-1){
// TSN already acked
// do nothing
return -1 //The acked TSN is less than the last acked
}
if(vl_tsn_comp==0){
// No new TSN are ACK-ed.
return 0
}
// New TSN's acked
// The remote side is alive, clear the retransmit counter
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter := 0
// remove the acked TSN-s from the tx chunk queue.
var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
var integer acked_bytes:=0
while((vl_idx!=-1) and (SCTP_compare_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].tsn,pl_ack)<1) ){
acked_bytes:=acked_bytes+SCTP_data_get_chunk_size(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].chunk)
// decrease the flight size
v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size-SCTP_data_get_chunk_size(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].chunk)
// remove the chunk
SCTP_Chunk_queue_pop(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue)
// next chunk idx
vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
}
//adjust the cwnd
if(v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd<v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh){
// slow-start case
if((v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+acked_bytes) >= (v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd-c_min_chunk_size)){
// The cwnd is fully utilized
v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:= v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd + SCTP_min(acked_bytes,v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu)
}
} else {
// congestion avoidance case
v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes := v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes + acked_bytes
if((v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+acked_bytes) >= (v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd-c_min_chunk_size)){
// The cwnd is fully utilized
v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:= v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd + v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu
v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes := v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes - v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd
}
}
// Remember to the last ack-ed
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn:= pl_ack
return 1 // 1: New TSN is acked
}
// Handles the incoming DATA
function SCTP_message_incoming_data_handler(in SCTP_Chunk pl_data, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
//log("data handler state, ", v_assoc_db.associations[pl_assoc_id].assoc_data.state)
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case (SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_SENT){
if(SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_data),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)>=0){
// put the chunk into the rx data chunk queue
SCTP_data_insert_into_rx(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue, // The queue
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn, // The first expected tsn of the queue, if empty
pl_data,
pl_assoc_id) // The chunk
} //else {
// The TSN is smaller than the last ack-ed
// just drop the chunk
// do nothing
//}
//log("return 1")
return 1
}
}
//log("return 0")
return 0
}
// Handles the incoming COOKIE-ECHO
function SCTP_message_incoming_cookie_echo_handler(in SCTP_CookieEcho_chunk pl_cookie_echo, // The chunk to process
in integer pl_assoc_id, // The association
inout SCTP_Packet pl_response, // place the cookie echo into this message, if DATA chunk is also received, the SACK will be added later
in boolean pl_cookie_preprocessed
) runs on SCTP_Engine_CT return integer {
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case (SCTP_ASSOC_ESTABLISHED){
// Process the messages and send answers if needed
if(pl_cookie_preprocessed){
// Only the cookie ack is should be sent
pl_response.chunks[lengthof(pl_response.chunks)]:={
cookie_ack:={
chunk_type := 11,
flags := '00000000'B,
chunk_length := 0
}
}
} else {
// TODO: Restart
}
}
}
return 0
}
// Handles the incoming COOKIE-ACK
function SCTP_message_incoming_cookie_ack_handler(in SCTP_Cookie_ack_chunk pl_cookie_ack, // The chunk to process
in integer pl_assoc_id // The association
) runs on SCTP_Engine_CT return integer {
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case (SCTP_ASSOC_COOKIE_ECHOED) {
v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_ESTABLISHED
// Start hearbeat
SCTP_hearbeat_start(pl_assoc_id)
// Stop the T1_cookie timer
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
// Notify the user
var SCTP_Notification_data vl_notification:={
assoc_id := pl_assoc_id,
notification:= {comm_up := {}},
options := {
{i_data_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported},
{reconfig_params:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params},
{forward_tsn_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported}
}
}
SCTP_congestion_set_init_params(pl_assoc_id)
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn := (v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn - 1) mod 4294967296
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
// Reset the retransmit counter
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
}
}
return 0
}
// Handles the incoming INIT-ACK chunk
function SCTP_message_incoming_init_ack_handler(in SCTP_InitAck_chunk pl_init_ack, // The chunk to process
in integer pl_assoc_id, // The association
in integer pl_chunk_idx // The index of the chunk in the incoming message
) runs on SCTP_Engine_CT return integer {
select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){
case (SCTP_ASSOC_COOKIE_WAIT) {
// Only the INIT ACK is acceptable
if( pl_chunk_idx == 0) {
// Find the cookie for echo
var integer vl_cookie_idx:=-1
var integer vl_supported_ext_idx:=-1
var integer vl_supported_forward_tsn_idx:=-1
if(ispresent(pl_init_ack.params)){
for(var integer vl_l:=0;vl_l<lengthof(pl_init_ack.params);vl_l:=vl_l+1){
if(ischosen(pl_init_ack.params[vl_l].state_cookie)){
vl_cookie_idx:=vl_l
}
if(ischosen(pl_init_ack.params[vl_l].supported_extensions)){
vl_supported_ext_idx:=vl_l
}
if(ischosen(pl_init_ack.params[vl_l].forward_tsn_supported)){
vl_supported_forward_tsn_idx:=vl_l
}
}
}
// Is there a cookie?
if(vl_cookie_idx==-1){
// No?
// Drop the packet
log("No cookie in the INIT_ACK. Dropped. ")
return -1;
}
// The INIT_ack seems OK
// Stop the timer
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
// Update the database
v_assoc_db.associations[pl_assoc_id].assoc_data:={
state:=SCTP_ASSOC_COOKIE_ECHOED,
remote_tag:=pl_init_ack.init_tag,
remote_tsn:=pl_init_ack.init_tsn,
remote_os:=pl_init_ack.os,
remote_mis:=pl_init_ack.mis,
remote_a_rwnd:=pl_init_ack.a_rwnd,
state_cookie:=pl_init_ack.params[vl_cookie_idx].state_cookie.state_cookie,
reconf_remote_seq_num:=pl_init_ack.init_tsn
}
// Check the extensions support
var boolean vl_idata:=false
var boolean vl_reconf:=false
if(vl_supported_ext_idx!=-1){
for(var integer vl_i:=0;vl_i<lengthof(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions);vl_i:=vl_i+1){
if(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions[vl_i]==64){ // IDATA chunk
vl_idata:=true
}
if(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions[vl_i]==130){ // IDATA chunk
vl_reconf:=true
}
}
}
v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported:=vl_idata and v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported
v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported:= vl_reconf and v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported
v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported:= (vl_supported_forward_tsn_idx!=-1) and v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported
// Send the Cookie Echo
SCTP_message_send_cookie_echo(pl_assoc_id)
// Reset the retransmit counter and start the timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T1_cookie,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
} else {
// Something else received
// Drop it
log("Unexpected packet, dorpped: ")
return -1
}
}
}
return 0
}
//Clean ups the acked and processed messages from the rx chunk queue
function SCTP_data_clean_tx_chunk_queue(inout SCTP_Chunk_queue_db_t pl_queue, in integer pl_assoc_id) runs on SCTP_Engine_CT {
var integer vl_chunk_idx:=pl_queue.first_element
while(vl_chunk_idx!=-1){
if( pl_queue.chunk_queue[vl_chunk_idx].mark_flag ){
// If the first element is marked, it is acked
SCTP_Chunk_queue_pop(pl_queue)
if(v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked==vl_chunk_idx){
v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked:=-1
break; // We reached the last acked chunk
} else {
vl_chunk_idx:=pl_queue.first_element
}
} else {
break; // The chunk can not be removed from the incoming queue
}
}
}
// Delivers the whole messages to the user
function SCTP_data_deliver(inout SCTP_msg_queue_list_t pl_msg_queue_list, in integer pl_assoc_id) runs on SCTP_Engine_CT {
// Iterate through the rx message queues and deliver the messages
for(var integer vl_idx:=0; vl_idx<lengthof(pl_msg_queue_list);vl_idx:=vl_idx+1){
// The ordered queue
var integer vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
while(vl_msg_idx!=-1){
// check the messages in the stream queue
if(SCTP_message_get_E_bit(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk)=='1'B){
// Whole message
var SCTP_MSG_data vl_msg_data
SCTP_data_get_msg_data(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk,vl_msg_data,pl_assoc_id) // Copy the msg
// Deliver it
p_sctp_ind_api.call(S_SCTP_Received_ind:{vl_msg_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
// Remove from the queue
SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages)
vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
} else {
if(not pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag){
break; // we reached the first not fully received message
} else {
// the msg are marked as skiped, skip it
SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages)
vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
}
}
}
// The unordered queue
vl_idx:=vl_idx+1
vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
while(vl_msg_idx!=-1){
// check the messages in the stream queue
if( (not pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag) // already processed
and (SCTP_message_get_E_bit(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk)=='1'B) // End flag
){
// Whole message
var SCTP_MSG_data vl_msg_data
SCTP_data_get_msg_data(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk,vl_msg_data,pl_assoc_id) // Copy the msg
// Deliver it
p_sctp_ind_api.call(S_SCTP_Received_ind:{vl_msg_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
// Mark the msg
pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag:=true
}
// detect the skiped element
// For unordered message no direct signal of the skiped messages
// But if there is a next message and the tsn of the last fragement < acked tsn
// the message is skiped.
// Not allowed to interleave two unordered message of the same queue
if((pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].next_element!=-1)
and (SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0)){
// Mark the msg
pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag:=true
}
if(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag // already processed
and (vl_msg_idx==pl_msg_queue_list[vl_idx].messages.first_element) // first of the queue
){
// remove it from the queue
SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages)
vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element
} else {
vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].next_element
}
}
}
}
// Insert the chunk into the rx queue
// pl_queue - the queue, where to insert the chunk
// pl_expected_tsn - If the queue is empty the new first chunk should use this tsn
// pl_chunk - the chunk to insert
function SCTP_data_insert_into_rx(inout SCTP_Chunk_queue_db_t pl_queue, in integer pl_expected_tsn,in SCTP_Chunk pl_chunk, in integer pl_assoc_id) runs on SCTP_Engine_CT {
var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked
var integer vl_tsn:=pl_expected_tsn
var integer vl_chunk_tsn:=SCTP_data_get_chunk_tsn(pl_chunk)
// set the index to pint to the first not acked item
if(vl_idx==-1){
// the last_acked was removed from the queue
// take the first element
vl_idx:=pl_queue.first_element
} else {
// pick the next one
vl_tsn:=SCTP_next_tsn(pl_queue.chunk_queue[vl_idx].tsn) // set the tsn counter
vl_idx:=pl_queue.chunk_queue[vl_idx].next_element
}
// find the place of the item
while(true){
if(vl_idx==-1){
// We need to add item
SCTP_Chunk_queue_push(pl_queue,c_empty_Chunk_queue_element)
vl_idx:=pl_queue.last_element
pl_queue.chunk_queue[vl_idx].tsn:=vl_tsn
}
if(pl_queue.chunk_queue[vl_idx].tsn == vl_chunk_tsn){
break; // we found the place
}
// move to the next item
vl_idx:=pl_queue.chunk_queue[vl_idx].next_element
// take next tsn
vl_tsn:=SCTP_next_tsn(vl_tsn)
}
// store the chunk
pl_queue.chunk_queue[vl_idx].chunk:=pl_chunk
}
// Construct the messages from the rx chunk queue
// scans the rx chunk queue for a messages
function SCTP_data_chunk2msg(inout SCTP_Chunk_queue_db_t pl_queue, inout SCTP_msg_queue_list_t pl_msg_queue_list, in integer pl_assoc_id) runs on SCTP_Engine_CT {
var integer vl_chunk_idx:=pl_queue.first_element
var boolean vl_gap_found:=false
while(vl_chunk_idx!=-1){
if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset)){
// See rfc6525 5.2.2. E2:
if(SCTP_compare_tsn(pl_queue.chunk_queue[vl_chunk_idx].tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset.last_assigned_tsn )>0){
break;
}
}
if(not pl_queue.chunk_queue[vl_chunk_idx].mark_flag){
// The chunk is not marked. Means was not put into the rx stream message queue
if(ispresent(pl_queue.chunk_queue[vl_chunk_idx].chunk)){
// Find the stream queue of the message
var integer vl_stream_idx:=-1; // index of the stream in the pl_msg_queue_list
var integer vl_stream_id:=SCTP_data_get_chunk_stream_id(pl_queue.chunk_queue[vl_chunk_idx].chunk) // The stream identifier
var integer vl_stream_seq_no
if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
vl_stream_id,
vl_stream_idx
)==-1){
// New incoming stream
// Is this the first message in the stream?
if( ((not vl_gap_found) // All of the previous chunk received, this is the first chunk of the first message
// of the stream. Or the remote side is sending a shit (or I forgot something)
or ( (SCTP_message_get_U_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk))=='1'B) ) // or first chunk of an unordered msg
and (SCTP_message_get_B_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk)=='1'B)
){
vl_stream_idx:=lengthof(pl_msg_queue_list)
// ordered queue
pl_msg_queue_list[vl_stream_idx]:=c_empty_msg_queue;
pl_msg_queue_list[vl_stream_idx].next_ssn:=-1; // Not used queue
// undered queue
pl_msg_queue_list[vl_stream_idx+1]:=c_empty_msg_queue;
pl_msg_queue_list[vl_stream_idx+1].next_ssn:=-1; // Not used queue
if(SCTP_int2int_add(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id,
vl_stream_id,
vl_stream_idx) == -1){
log("Intenal error")
}
} else {
vl_stream_idx:=-1 // We can't be sure that this is the first message of the stream
}
}
if(vl_stream_idx!=-1){
// valid stream
if(SCTP_message_get_U_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk)=='0'B){
//ordered
if(pl_msg_queue_list[vl_stream_idx].next_ssn!=-1 or (not vl_gap_found)){
// In the case of the ordered message
// Try to store the first message if all of the previous chunk has been received.
SCTP_data_find_store_chunk(pl_msg_queue_list[vl_stream_idx],pl_queue.chunk_queue[vl_chunk_idx],false)
}
} else {
//unordered
SCTP_data_find_store_chunk(pl_msg_queue_list[vl_stream_idx+1],pl_queue.chunk_queue[vl_chunk_idx],true)
}
}
if(SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_queue.chunk_queue[vl_chunk_idx].chunk), v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0){
// if the tsn of the packet is already ack-ed mark it for clean up
pl_queue.chunk_queue[vl_chunk_idx].mark_flag:=true
}
} else {
// found a gap
vl_gap_found:=true
}
}
vl_chunk_idx:=pl_queue.chunk_queue[vl_chunk_idx].next_element
}
}
// Stores the message in the msg queue if applicable
function SCTP_data_find_store_chunk(inout SCTP_msg_queue_t pl_msg_db, inout SCTP_Chunk_queue_element_t pl_chunk, in boolean pl_unordered){
if(((pl_msg_db.next_ssn==-1) or // not used queue
(pl_msg_db.next_ssn==SCTP_data_get_chunk_ssn(pl_chunk.chunk)) // next expected message
or pl_unordered) // Unordered
and
(SCTP_message_get_B_bit(pl_chunk.chunk)=='1'B) // First chunk of the message
){
// unused queue, first chunk of the message
// or the first chunk of the next expected message
// Add the chunk as the first chunk of the next message
SCTP_Chunk_queue_push(pl_msg_db.messages,pl_chunk)
pl_chunk.mark_flag:=true // Chunk is in the rx stream message queue
pl_msg_db.next_ssn:=SCTP_next_ssn(SCTP_data_get_chunk_ssn(pl_chunk.chunk)) // Store the next ssn of the stream
pl_msg_db.messages.chunk_queue[pl_msg_db.messages.last_element].tsn:=SCTP_next_tsn(SCTP_data_get_chunk_reassembly_seq_no(pl_chunk.chunk)) // store the next TSN/FSN
return
}
// Find the message
var integer vl_idx:=pl_msg_db.messages.first_element
while(vl_idx!=-1){
if(SCTP_data_get_chunk_ssn(pl_msg_db.messages.chunk_queue[vl_idx].chunk) == SCTP_data_get_chunk_ssn(pl_chunk.chunk)){
// This is the message we're looking for
// Is this the next chunk?
if(pl_msg_db.messages.chunk_queue[vl_idx].tsn== SCTP_data_get_chunk_reassembly_seq_no(pl_chunk.chunk)){
// yes, this is
// append the data part.
SCTP_data_chunk_append_data(pl_msg_db.messages.chunk_queue[vl_idx].chunk,pl_chunk.chunk)
// updated the stored tsn of the message for support the RFC3758
SCTP_data_chunk_copy_tsn(pl_msg_db.messages.chunk_queue[vl_idx].chunk,pl_chunk.chunk)
pl_chunk.mark_flag:=true // Chunk is in the rx stream message queue
pl_msg_db.messages.chunk_queue[vl_idx].tsn := SCTP_next_tsn(pl_msg_db.messages.chunk_queue[vl_idx].tsn) // store th enext expected TSN/FSN
// Set E bit if needed
if(SCTP_message_get_E_bit(pl_chunk.chunk)=='1'B){
SCTP_message_set_E_bit('1'B,pl_msg_db.messages.chunk_queue[vl_idx].chunk)
}
}
return
}
vl_idx:=pl_msg_db.messages.chunk_queue[vl_idx].next_element
}
}
// Generates SACK from the rx queue
// pl_queue - the queue, where to insert the chunk
// pl_remote_tsn - The last acked tsn+1
// pl_chunk - the chunk to return the SACK
function SCTP_data_gen_sack(in SCTP_Chunk_queue_db_t pl_queue, in integer pl_remote_tsn,out SCTP_Chunk pl_chunk, in integer pl_assoc_id) runs on SCTP_Engine_CT {
var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked
// set the index to point to the first not acked item
//log("*************************")
//log(pl_queue)
//log(pl_remote_tsn)
//log(pl_assoc_id)
//log("*************************")
if(vl_idx==-1){
// the last_acked was removed from the queue
// take the first element
vl_idx:=pl_queue.first_element
if(vl_idx!=-1 and
((not ispresent(pl_queue.chunk_queue[vl_idx].chunk)) or pl_queue.chunk_queue[vl_idx].mark_flag )
){
// The first element represents a non marked hole
vl_idx:=-1 // no new chunk to ack
}
}
// find the last element before a hole/end
while( (vl_idx != -1) and // valid element
(pl_queue.chunk_queue[vl_idx].next_element!=-1) and // has a next element
(ispresent(pl_queue.chunk_queue[pl_queue.chunk_queue[vl_idx].next_element].chunk) or pl_queue.chunk_queue[pl_queue.chunk_queue[vl_idx].next_element].mark_flag)// wich is not a hole
){
vl_idx:=pl_queue.chunk_queue[vl_idx].next_element // advance to it
}
var integer vl_sack_tsn
if(vl_idx != -1){
vl_sack_tsn:=pl_queue.chunk_queue[vl_idx].tsn
} else {
vl_sack_tsn:= SCTP_prev_tsn(pl_remote_tsn)
}
pl_chunk.sack:={
chunk_type:= 3,
flags := '00000000'B,
chunk_length:=0,
cum_tsn_ack:=vl_sack_tsn,
a_rwnd:=65535,
no_of_gap_blocks:=0,
no_of_dup_tsn:=0,
gap_blocks:=omit,
dup_tsns:=omit
}
// set the pointers
v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked:=vl_idx
v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(vl_sack_tsn)
}
//Process the incoming INIT & COOKIE ECHO
function SCTP_message_incoming_init_cookie_handler(in integer pl_assoc_id,
in integer pl_transport_id,
in SCTP_Packet pl_sctp_packet) runs on SCTP_Engine_CT {
if(ischosen(pl_sctp_packet.chunks[0].init)){
// INIT chunk received. Generate INIT-ACK
var SCTP_Packet vl_response := c_empty_sctp_packet
var SCTP_InitAck_chunk vl_initack_chunk := c_empty_init_ack_chunk
var integer vl_init_tag:=SCTP_rnd_32bit_int()
//Check the supported extensions
var boolean vl_idata:=false
var boolean vl_reconf:=false
var boolean vl_fwd_tsn:=false
if(ispresent(pl_sctp_packet.chunks[0].init.params)){
for(var integer vl_i:=0;vl_i<lengthof(pl_sctp_packet.chunks[0].init.params);vl_i:=vl_i+1){
if(ischosen(pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions)){
for(var integer vl_k:=0;vl_k<lengthof(pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions);vl_k:=vl_k+1){
if((pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions[vl_k]==64)){
vl_idata:=true
}
if((pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions[vl_k]==130)){
vl_reconf:=true
}
}
} else if(ischosen(pl_sctp_packet.chunks[0].init.params[vl_i].forward_tsn_supported)){
vl_fwd_tsn:=true
}
}
}
vl_idata:=vl_idata and v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported // support if requested both by the user and remote side
vl_reconf:=vl_reconf and v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported // support if requested both by the user and remote side
vl_fwd_tsn :=vl_fwd_tsn and v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported
var octetstring pl_cookie:=SCTP_gen_state_cookie(v_assoc_db.associations[pl_assoc_id].assoc_data.cookie_lifespan,
vl_init_tag,// pl_own_init_tag,
pl_sctp_packet.chunks[0].init.init_tag, // pl_remote_init_tag,
0,// pl_tie_tag, 0 means no tie
pl_sctp_packet.chunks[0].init.init_tsn, // pl_remote_tsn,
pl_sctp_packet.chunks[0].init.os, // pl_remote_os,
pl_sctp_packet.chunks[0].init.mis ,// pl_remote_mis,
pl_sctp_packet.chunks[0].init.a_rwnd, // pl_remote_a_rwnd,
vl_idata,
vl_reconf,
vl_fwd_tsn ,
v_secret_key // pl_key
)
vl_initack_chunk:={
init_tag := vl_init_tag,
a_rwnd := 65535,
os := 65535,
mis := 65535,
init_tsn := vl_init_tag,
params :={
{state_cookie:={param_type:=7,param_length:=0,state_cookie:=pl_cookie}}
}
}
if(vl_idata or vl_reconf){
var integer vl_idx:=0
if(ispresent(vl_initack_chunk.params)){
vl_idx:=lengthof(vl_initack_chunk.params)
}
vl_initack_chunk.params[vl_idx].supported_extensions:={
param_type := 32776,
param_length :=0,
supported_extensions:={}
}
if(vl_idata){
vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=64
}
if(vl_reconf){
vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=130
}
}
if(vl_fwd_tsn){
var integer vl_idx:=0
if(ispresent(vl_initack_chunk.params)){
vl_idx:=lengthof(vl_initack_chunk.params)
}
vl_initack_chunk.params[vl_idx].forward_tsn_supported:={
param_type := 49152,
param_length :=0
}
}
vl_response:={
common_header := {
source_port:=pl_sctp_packet.common_header.destination_port,
destination_port:=pl_sctp_packet.common_header.source_port,
verification_tag:=pl_sctp_packet.chunks[0].init.init_tag
},
chunks := { {init_ack:=vl_initack_chunk} }
}
SCTP_send_packet(pl_transport_id,vl_response);
} else {
// Cookie Echo received, process it.
// Allocate new assoc
var integer vl_new_assoc:=SCTP_assoc_construct_from_cookie_echo(pl_sctp_packet,pl_transport_id,pl_assoc_id);
if(vl_new_assoc < 0){ // The cookie check failed
// drop the packet
return;
}
// Check the cookie lifetime
if(v_assoc_db.associations[vl_new_assoc].assoc_data.cookie_validity==1){ // Cookie expired
// Send abort with erro chunk
// Construct the error chunk
var SCTP_error_chunk vl_error_chunk:= {
chunk_type := 9,
flags := '00000000'B,
chunk_length := 0,
params := {
{stale_cookie:={cause_code:=3, cause_length:=0,measure:=0}}
}
}
// send the abort
SCTP_message_send_abort(pl_transport_id,pl_sctp_packet,vl_new_assoc,-,{{error_:=vl_error_chunk}})
// drop the association
SCTP_free_assoc_entry(vl_new_assoc)
return; // We're done
}
// Add the connection to the id map
if(SCTP_add_id_map(vl_new_assoc,pl_transport_id,v_assoc_db.associations[vl_new_assoc].assoc_data.local_port,v_assoc_db.associations[vl_new_assoc].assoc_data.remote_port)==-1){
// Stop further processing
return;
}
// Send connected
var SCTP_Connected_data vl_connected:={
assoc_id := vl_new_assoc,
local_sctp_port := v_assoc_db.associations[vl_new_assoc].assoc_data.local_port,
remote_sctp_port := v_assoc_db.associations[vl_new_assoc].assoc_data.remote_port,
listen_assoc_id := pl_assoc_id
}
p_sctp_ind_api.call(S_SCTP_Connected_ind:{vl_connected},nowait) to v_assoc_db.associations[vl_new_assoc].assoc_data.rem_comp
// Send notification to the owner of the listen port
var SCTP_Notification_data vl_notification:={
assoc_id := vl_new_assoc,
notification:= {comm_up := {}},
options := {
{i_data_supported:=v_assoc_db.associations[vl_new_assoc].assoc_data.i_data_supported},
{reconfig_params:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params},
{forward_tsn_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported}
}
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[vl_new_assoc].assoc_data.rem_comp
v_assoc_db.associations[vl_new_assoc].assoc_data.state:=SCTP_ASSOC_ESTABLISHED;
SCTP_hearbeat_start(vl_new_assoc)
// Reset the retransmit counter
v_assoc_db.associations[vl_new_assoc].assoc_data.retransmit_counter:=0
SCTP_congestion_set_init_params(vl_new_assoc)
// Set the last acked TSN to the expeted -1
v_assoc_db.associations[vl_new_assoc].assoc_data.remote_last_sack_tsn := (v_assoc_db.associations[vl_new_assoc].assoc_data.own_tsn - 1) mod 4294967296
// Send the packet for further processing
SCTP_message_incoming_handler(vl_new_assoc,pl_sctp_packet,true)
}
}
// Process the Out of the blue packets
// See 8.4 of rfc
function SCTP_message_process_oob_packet(in integer pl_transport_id, // Where to send the abort
in SCTP_Packet pl_sctp_packet // The incoming packet
) runs on SCTP_Engine_CT {
// first search map the chunks
// Interested ones:
// - Abort
var boolean vl_abort_found:=false
// - Init
var boolean vl_init_found:=false
// - Cookie Echo
var boolean vl_cookie_echo_found:=false
// - Shutdown Complete
var boolean vl_shutdown_complete_found:=false
// - Shutdown Ack
var boolean vl_shutdown_ack_found:=false
// - CookieAck
var boolean vl_cookie_ack_found:=false
var integer vl_i:=0
for(vl_i:=0;vl_i<lengthof(pl_sctp_packet.chunks);vl_i:=vl_i+1) {
if(ischosen(pl_sctp_packet.chunks[vl_i].abort)){
vl_abort_found:=true
continue
} else if (ischosen(pl_sctp_packet.chunks[vl_i].init)) {
vl_init_found:=true
continue
} else if (ischosen(pl_sctp_packet.chunks[vl_i].cookie_echo)) {
vl_cookie_echo_found:=true
continue
} else if (ischosen(pl_sctp_packet.chunks[vl_i].shutdown_complete)) {
vl_shutdown_complete_found:=true
continue
} else if (ischosen(pl_sctp_packet.chunks[vl_i].shutdown_ack)) {
vl_shutdown_ack_found:=true
continue
} else if (ischosen(pl_sctp_packet.chunks[vl_i].cookie_ack)) {
vl_cookie_ack_found:=true
continue
}
}
// The packet should be dropped if
// - Abort
// - CookieAck
// - Shutdown Complete
if( vl_abort_found or vl_cookie_ack_found or vl_shutdown_complete_found){
return; // do nothing
} else if(vl_init_found){
// Init -> send abort - tag handled automatically
SCTP_message_send_abort(pl_transport_id,pl_sctp_packet)
} else if(vl_cookie_echo_found) {
// Cookie Echo -> Send abort, but check the cookie first
// construct a temp assoc entry, it checks the validity of the cookie also
var integer pl_temp_id:=SCTP_assoc_construct_from_cookie_echo(pl_sctp_packet,pl_transport_id);
if(pl_temp_id>=0){
// send abort
SCTP_message_send_abort(pl_transport_id,-,pl_temp_id);
// free the assoc entry
SCTP_free_assoc_entry(pl_temp_id);
}
// else just drop the message
} else if(vl_shutdown_ack_found) {
// SHUTDOWN ACK -> send SHUTDOWN COMPLETE
SCTP_message_send_shutdown_complete(pl_transport_id,pl_sctp_packet)
} else {
// any other case: send abort
SCTP_message_send_abort(pl_transport_id,pl_sctp_packet)
}
}
// Send the shutdown ack message
function SCTP_message_send_shutdown_ack(in integer pl_assoc_id) runs on SCTP_Engine_CT {
var SCTP_Packet vl_sctp := c_empty_sctp_packet
vl_sctp:={
common_header:={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
},
chunks:={{shutdown_ack:={chunk_type:=8,flags:='00000000'B,chunk_length:=0}}}
}
// Send it
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
}
// Send the shutdown message
function SCTP_message_send_shutdown(in integer pl_assoc_id) runs on SCTP_Engine_CT {
var SCTP_Packet vl_sctp := c_empty_sctp_packet
vl_sctp:={
common_header:={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
},
chunks:={{shutdown:={chunk_type:=7,flags:='00000000'B,chunk_length:=0,cum_tsn_ack:=SCTP_prev_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)}}}
}
// Send it
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
}
function SCTP_message_send_heartbeat(in integer pl_assoc_id) runs on SCTP_Engine_CT {
var SCTP_Packet vl_sctp := c_empty_sctp_packet
vl_sctp:={
common_header:={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
},
chunks:={{heartbeat:={chunk_type:=4,
flags:='00000000'B,
chunk_length:=0,
params:={{heartbeat_info:={ param_type:=1,
param_length := 0,
info:=SCTP_misc_float2oct(t_run_time.read)
}}}
}}}
}
// Send it
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
}
// Send abort message
// The abort message can be constructed from
// -- assoc data
// -- incoming sctp packet
function SCTP_message_send_abort(in integer pl_transport_id:=-1, // Where to send the abort
in SCTP_Packet pl_sctp_packet := c_empty_sctp_packet, // The incoming packet caused the abort
in integer pl_assoc_id:=-1, // The assoc id if applicable
in SCTP_Error_cause_list pl_cause_list:= {}, // the cause the abort
in SCTP_Chunk_List pl_chunk_list :={} // The chunks include before the abort chunk
) runs on SCTP_Engine_CT {
var SCTP_Packet vl_response := c_empty_sctp_packet
var SCTP_abort_chunk vl_abort_chunk := c_empty_abort_chunk
if(pl_assoc_id != -1){
// We have association
// Use the data from the database
pl_transport_id := v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id
vl_response.common_header:={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
}
vl_abort_chunk.T_flag:='0'B // The verification_tag is not reflected
} else {
// Out of the blue packet
if( (lengthof(pl_sctp_packet.chunks) > 0)
and ischosen(pl_sctp_packet.chunks[0].init)
){
// INIT chunk, get the Initiate Tag for the verification_tag
vl_response.common_header:={
source_port:=pl_sctp_packet.common_header.destination_port,
destination_port:=pl_sctp_packet.common_header.source_port,
verification_tag:=pl_sctp_packet.chunks[0].init.init_tag
}
vl_abort_chunk.T_flag:='0'B // The verification_tag is not reflected
} else {
// reflect the verification_tag
vl_response.common_header:={
source_port:=pl_sctp_packet.common_header.destination_port,
destination_port:=pl_sctp_packet.common_header.source_port,
verification_tag:=pl_sctp_packet.common_header.verification_tag
}
vl_abort_chunk.T_flag:='1'B // The verification_tag is reflected
}
}
// Construct the message
// Add cause list if needed
if(lengthof(pl_cause_list) > 0){
vl_abort_chunk.params:=pl_cause_list
}
// Add the other chunks before the abort chunk
if(lengthof(pl_chunk_list) > 0){
vl_response.chunks:=pl_chunk_list
}
// Add the abort chunk
vl_response.chunks[lengthof(vl_response.chunks)].abort:=vl_abort_chunk;
// Send it
SCTP_send_packet(pl_transport_id,vl_response);
}
// Send abort message
// The shutdown complete message can be constructed from
// -- assoc data
// -- incoming sctp packet
function SCTP_message_send_shutdown_complete(in integer pl_transport_id:=-1, // Where to send
in SCTP_Packet pl_sctp_packet := c_empty_sctp_packet, // The incoming packet
in integer pl_assoc_id:=-1 // The assoc id if applicable
) runs on SCTP_Engine_CT {
var SCTP_Packet vl_response := c_empty_sctp_packet
var SCTP_ShutdownComplete_chunk vl_sh_comp_chunk := c_empty_shutdown_complete_chunk
if(pl_assoc_id != -1){
// We have association
// Use the data from the database
pl_transport_id := v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id
vl_response.common_header:={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
}
vl_sh_comp_chunk.T_flag:='0'B // The verification_tag is not reflected
} else {
// Out of the blue packet
// reflect the verification_tag
vl_response.common_header:={
source_port:=pl_sctp_packet.common_header.destination_port,
destination_port:=pl_sctp_packet.common_header.source_port,
verification_tag:=pl_sctp_packet.common_header.verification_tag
}
vl_sh_comp_chunk.T_flag:='1'B // The verification_tag is reflected
}
// Construct the message
// Add the chunk
vl_response.chunks[lengthof(vl_response.chunks)].shutdown_complete:=vl_sh_comp_chunk;
// Send it
SCTP_send_packet(pl_transport_id,vl_response);
}
// Send the sctp packet
function SCTP_send_packet(in integer pl_transport_id, // Where to send the packet
in SCTP_Packet pl_sctp_packet) runs on SCTP_Engine_CT {
var SCTP_Engine_packet vl_packet:={
transport_id:=pl_transport_id,
sctp_packet:=f_SCTP_enc(pl_sctp_packet)
}
p_packet_port.send(vl_packet)
}
// Send message with Cookie Echo
function SCTP_message_send_cookie_echo(in integer pl_assoc_id) runs on SCTP_Engine_CT {
var SCTP_Packet vl_sctp := c_empty_sctp_packet
vl_sctp:={
common_header :={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag := v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
},
chunks:={{cookie_echo:={chunk_type:=10, flags:= '00000000'B, chunk_length:=0,cookie:=v_assoc_db.associations[pl_assoc_id].assoc_data.state_cookie}}}
}
// Send the packet
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp)
}
/*****************************************************************************/
//
// API functions
//
/*****************************************************************************/
// Handles the reconfig request
function SCTP_api_handle_reconfig(in SCTP_Reconfiguration_req vl_reconfig, // the listen parameters
in SCTP_Engine_CT vl_sender // The sender of the request
) runs on SCTP_Engine_CT {
var integer vl_assoc_id:=vl_reconfig.assoc_id
// Check the associ id validity
if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary
or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used?
or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc?
){
// invalid assoc_id
p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender
return
}
// check the state of the association
if(v_assoc_db.associations[vl_assoc_id].assoc_data.state!=SCTP_ASSOC_ESTABLISHED){
// The sendrequest is allowed only in established state
p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is not in the established state"}) to vl_sender
return
}
// Check if request is ongoing
if(v_assoc_db.associations[vl_assoc_id].assoc_data.reconfig_ongoing){
p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_RECONFIG_ONGOING,"There is an ongoing reconfig request. Please try again later"}) to vl_sender
return
}
var SCTP_Packet vl_sctp := c_empty_sctp_packet
vl_sctp:={
common_header :={
source_port:=v_assoc_db.associations[vl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[vl_assoc_id].assoc_data.remote_port,
verification_tag := v_assoc_db.associations[vl_assoc_id].assoc_data.remote_tag
},
chunks:={{
re_config:={
chunk_type:=130,
flags:='00000000'B,
chunk_length:=0,
params:={}
}
}}
}
// create and send the request
if(ischosen(vl_reconfig.reconf_method.assoc_reset)){
vl_sctp.chunks[0].re_config.params[0]:={
ssn_tsn_reset_req:={
param_type:=15,
param_length:=0,
reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
}
}
v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
} else if(ischosen(vl_reconfig.reconf_method.stream_reset)){
if(vl_reconfig.reconf_method.stream_reset.incoming){
vl_sctp.chunks[0].re_config.params[0]:={
in_ssn_reset_req:={
param_type:=14,
param_length:=0,
reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num,
stream_numbers:=vl_reconfig.reconf_method.stream_reset.streams
}
}
v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
} else {
if(ispresent(vl_reconfig.reconf_method.stream_reset.streams)){
SCTP_reconf_generate_out_ssn_reset_req( vl_reconfig.reconf_method.stream_reset.streams,
SCTP_prev_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.own_tsn),
vl_sctp.chunks[0].re_config,
vl_assoc_id
)
} else {
SCTP_reconf_generate_out_ssn_reset_req( {},
SCTP_prev_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.own_tsn),
vl_sctp.chunks[0].re_config,
vl_assoc_id
)
}
v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
}
} else {
// add stream req
if(vl_reconfig.reconf_method.add_stream.incoming){
vl_sctp.chunks[0].re_config.params[0]:={
add_in_stream:={
param_type:=18,
param_length:=0,
reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num,
stream_num:=vl_reconfig.reconf_method.add_stream.new_streams,
reserved:=0
}
}
v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
} else {
vl_sctp.chunks[0].re_config.params[0]:={
add_out_stream:={
param_type:=17,
param_length:=0,
reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num,
stream_num:=vl_reconfig.reconf_method.add_stream.new_streams,
reserved:=0
}
}
v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num
}
}
v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num:=SCTP_next_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num)
// Send the packet
SCTP_send_packet(v_assoc_db.associations[vl_assoc_id].assoc_data.transport_id,vl_sctp)
}
// Handles the shutdown_conf request
function SCTP_api_handle_shutdown_conf (in integer vl_assoc_id, // the listen parameters
in SCTP_Engine_CT vl_sender // The sender of the request
) runs on SCTP_Engine_CT {
if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary
or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used?
or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc?
){
// invalid assoc_id
p_sctp_req_api.raise(S_SCTP_Shutdown_conf, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender
return
}
// check the state of the association
if(v_assoc_db.associations[vl_assoc_id].assoc_data.state!=SCTP_ASSOC_CLOSED){
// The sendrequest is allowed only in established state
p_sctp_req_api.raise(S_SCTP_Shutdown_conf, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is not in closed state"}) to vl_sender
return
}
// Free the association
SCTP_free_assoc_entry(vl_assoc_id)
// Confirm the request
p_sctp_req_api.reply(S_SCTP_Shutdown_conf:{?} value {vl_assoc_id}) to vl_sender
}
// Handles the shutdown request
function SCTP_api_handle_shutdown (in SCTP_Shutdown_data pl_shutdown_data, // the shutdown parameters
in SCTP_Engine_CT vl_sender // The sender of the request
) runs on SCTP_Engine_CT {
var integer vl_assoc_id:=pl_shutdown_data.assoc_id
// Check the associ id validity
if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary
or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used?
or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc?
){
// invalid assoc_id
p_sctp_req_api.raise(S_SCTP_Shutdown, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender
return
}
// check the state of the association
if(v_assoc_db.associations[vl_assoc_id].assoc_data.state==SCTP_ASSOC_CLOSED){
// The sendrequest is allowed only in established state
p_sctp_req_api.raise(S_SCTP_Shutdown, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is already in closed state"}) to vl_sender
return
}
if(not pl_shutdown_data.graceful_shutdown){
// Abort was requested
if(ispresent(pl_shutdown_data.reason)){
// send the abort with the reason
SCTP_message_send_abort(-,-,vl_assoc_id,{{user_abort:={cause_code:=12, cause_length:=0,reason:=char2oct(pl_shutdown_data.reason)}}})
} else {
SCTP_message_send_abort(-,-,vl_assoc_id)
}
// Confirm the request
p_sctp_req_api.reply(S_SCTP_Shutdown:{?} value {vl_assoc_id}) to vl_sender
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=vl_assoc_id,
graceful_shutdown := false,
reason := "Abort request has been sent."
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(vl_assoc_id)
} else {
select(v_assoc_db.associations[vl_assoc_id].assoc_data.state){
case ( (SCTP_ASSOC_COOKIE_WAIT,SCTP_ASSOC_COOKIE_ECHOED)){
// We're in th eset up phase
// Abort it
SCTP_message_send_abort(-,-,vl_assoc_id)
// Confirm the request
p_sctp_req_api.reply(S_SCTP_Shutdown:{?} value {vl_assoc_id}) to vl_sender
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=vl_assoc_id,
graceful_shutdown := false,
reason := "Association set up has been aborted."
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(vl_assoc_id)
}
case (SCTP_ASSOC_ESTABLISHED ){
// Set the state to shutdown pending
v_assoc_db.associations[vl_assoc_id].assoc_data.state:=SCTP_ASSOC_SHUTDOWN_PENDING
// Confirm the request
p_sctp_req_api.reply(S_SCTP_Shutdown:{?} value {vl_assoc_id}) to vl_sender
// Call the data scheduler. It will send the SHUTDOWN if possible
SCTP_data_send_scheduler(vl_assoc_id)
}
case else {
// We're already in the shutdown phase
// Confirm the request
p_sctp_req_api.reply(S_SCTP_Shutdown:{?} value {vl_assoc_id}) to vl_sender
// Nothing more to do here
}
}
}
}
// Handles the send msg request
function SCTP_api_handle_send_msg (in SCTP_MSG_data pl_send_msg_data, // the send req parameters
in SCTP_Engine_CT vl_sender // The sender of the request
) runs on SCTP_Engine_CT {
var integer vl_assoc_id:=pl_send_msg_data.assoc_id
// Check the associ id validity
if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary
or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used?
or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc?
){
// invalid assoc_id
p_sctp_req_api.raise(S_SCTP_Send_req, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender
return
}
// check the state of the association
if(v_assoc_db.associations[vl_assoc_id].assoc_data.state!=SCTP_ASSOC_ESTABLISHED){
// The sendrequest is allowed only in established state
p_sctp_req_api.raise(S_SCTP_Send_req, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is not in the established state"}) to vl_sender
return
}
// put the message into th estream queue
var integer vl_stream_id:=pl_send_msg_data.stream_id
var integer vl_queue_id
// get the queue id
if(SCTP_int2int_get(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_stream_idx_map_id,
vl_stream_id,
vl_queue_id
)==-1){
// No queue for that stream
// create one
vl_queue_id:=lengthof(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues)
// ordered queue
v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id]:=c_empty_msg_queue
// unordered queue
v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id+1]:=c_empty_msg_queue
// register it in the map
if(SCTP_int2int_add(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_stream_idx_map_id,
vl_stream_id,
vl_queue_id) == -1){
// WTF???
p_sctp_req_api.raise(S_SCTP_Send_req, SCTP_operation_execption:{SCTP_ENG_INTERNAL_ERROR,"INTERNAL Error: wrong map id SCTP_api_handle_send_msg SCTP_int2int_add"}) to vl_sender
return
}
}
var boolean vl_ordered:= true
var bitstring vl_uflag:= '0'B
// Put the msg into that queue
var SCTP_Chunk_queue_element_t vl_msg_element:=c_empty_Chunk_queue_element
// Is it unordered?
if(ispresent(pl_send_msg_data.options)){
for(var integer vl_x:=0;vl_x<lengthof(pl_send_msg_data.options); vl_x:=vl_x+1){
if(ischosen(pl_send_msg_data.options[vl_x].unordered)){
vl_ordered:= false // not ordered
vl_queue_id:=vl_queue_id+1 // adjust the queue id
vl_uflag:= '1'B
} else if(ischosen(pl_send_msg_data.options[vl_x].lifetime)){
vl_msg_element.lifetime:=t_run_time.read+pl_send_msg_data.options[vl_x].lifetime
} else if(ischosen(pl_send_msg_data.options[vl_x].max_retransmit_counter)){
vl_msg_element.counter:=pl_send_msg_data.options[vl_x].max_retransmit_counter
}
}
}
if(v_assoc_db.associations[vl_assoc_id].assoc_data.i_data_supported){
// Use IData
vl_msg_element:={
chunk:={
idata:={
chunk_type:= 64,
E_flag := '0'B,
B_flag := '0'B,
U_flag := vl_uflag,
I_flag := '0'B,
res_flags := '0000'B,
chunk_length := 0,
tsn := 0,
stream_id := vl_stream_id,
reserved := 0,
mid := v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn,
ppid_fsn := pl_send_msg_data.ppid,
user_data := pl_send_msg_data.data
}
}
}
// next_ssn means: next message id
v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn :=SCTP_next_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn)
} else {
// Normal DATA chunk
vl_msg_element:={
chunk:={
data:={
chunk_type:= 0,
E_flag := '0'B,
B_flag := '0'B,
U_flag := vl_uflag,
I_flag := '0'B,
res_flags := '0000'B,
chunk_length := 0,
tsn := 0,
stream_id := vl_stream_id,
ssn := v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn,
ppid := pl_send_msg_data.ppid,
user_data := pl_send_msg_data.data
}
}
}
// Adjust the next ssn
if(vl_ordered){
// The unordered doesn't have ssn
v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn :=SCTP_next_ssn(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].next_ssn)
}
}
SCTP_Chunk_queue_push(v_assoc_db.associations[vl_assoc_id].assoc_data.tx_msg_queues[vl_queue_id].messages,vl_msg_element)
// send back the result
p_sctp_req_api.reply(S_SCTP_Send_req:{?} value {vl_assoc_id}) to vl_sender
// Invoke the send scheduler
SCTP_data_send_scheduler(vl_assoc_id)
}
// Handles the listen request
function SCTP_api_handle_listen(in SCTP_Listen_data pl_listen_data, // the listen parameters
in SCTP_Engine_CT vl_sender // The sender of the request
) runs on SCTP_Engine_CT {
// Basic check
if((pl_listen_data.local_sctp_port<1) or (pl_listen_data.local_sctp_port>65535)){
// The listen port is out of the range
p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_WRONG_PORT,"Listen port is out of the allowed range 1..65535"}) to vl_sender
// Stop further processing
return;
}
// Check transport id
if(pl_listen_data.transport_id<-1){
// The transport id should be >=0 or -1 (all connection)
p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_WRONG_PARAMETERS,"The transport id should be >=0 or -1 (all connection)"}) to vl_sender
// Stop further processing
return;
}
// Is the port is used?
if(SCTP_find_id_map(pl_listen_data.transport_id,pl_listen_data.local_sctp_port,0)!=-1){
// The port is already used for listen
p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_PORT_ALREADY_USED,"The port is already used."}) to vl_sender
// Stop further processing
return;
}
// create a new assoc entry
var integer vl_new_assoc:=SCTP_allocate_assoc_entry();
// Fill the data
v_assoc_db.associations[vl_new_assoc].assoc_data:={
state := SCTP_ASSOC_LISTEN,
transport_id :=pl_listen_data.transport_id ,
local_port:=pl_listen_data.local_sctp_port,
remote_port:=0,
rem_comp:=vl_sender
}
// Add it to the map
if(SCTP_add_id_map(vl_new_assoc,pl_listen_data.transport_id,pl_listen_data.local_sctp_port,0)==-1){
// Something went wrong
// free the assoc entry
SCTP_free_assoc_entry(vl_new_assoc);
// Raise the exception
p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_INTERNAL_ERROR,"Internal error. SCTP_add_id_map -1"}) to vl_sender
// Stop further processing
return;
}
// process the options
if(ispresent(pl_listen_data.options)){
var integer k
for(k:=0;k<lengthof(pl_listen_data.options);k:=k+1){
if(ischosen(pl_listen_data.options[k].i_data_supported)){
v_assoc_db.associations[vl_new_assoc].assoc_data.i_data_supported:=pl_listen_data.options[k].i_data_supported
}
if(ischosen(pl_listen_data.options[k].forward_tsn_supported)){
v_assoc_db.associations[vl_new_assoc].assoc_data.forward_tsn_supported:=pl_listen_data.options[k].forward_tsn_supported
}
if(ischosen(pl_listen_data.options[k].reconfig_params)){
v_assoc_db.associations[vl_new_assoc].assoc_data.reconf_params:=pl_listen_data.options[k].reconfig_params
}
}
}
// return the result
p_sctp_req_api.reply(S_SCTP_Listen:{?} value {vl_new_assoc}) to vl_sender
}
// Handles the connect request
function SCTP_api_handle_connect(in SCTP_Connect_data pl_connect_data , // the connect parameters
in SCTP_Engine_CT vl_sender // The sender of the request
) runs on SCTP_Engine_CT {
// Check the port numbers
if((pl_connect_data.local_sctp_port<1) or (pl_connect_data.local_sctp_port>65535)){
// The listen port is out of the range
p_sctp_req_api.raise(S_SCTP_Connect, SCTP_operation_execption:{SCTP_ENG_WRONG_PORT,"Listen port is out of the allowed range 1..65535"}) to vl_sender
// Stop further processing
return;
}
if((pl_connect_data.remote_sctp_port<1) or (pl_connect_data.remote_sctp_port>65535)){
// The listen port is out of the range
p_sctp_req_api.raise(S_SCTP_Connect, SCTP_operation_execption:{SCTP_ENG_WRONG_PORT,"Listen port is out of the allowed range 1..65535"}) to vl_sender
// Stop further processing
return;
}
// Check transport id
if(pl_connect_data.transport_id<0){
// The transport id should be >=0
p_sctp_req_api.raise(S_SCTP_Connect, SCTP_operation_execption:{SCTP_ENG_WRONG_PARAMETERS,"The transport id should be >=0"}) to vl_sender
// Stop further processing
return;
}
// Is the port is used?
if(SCTP_find_id_map(pl_connect_data.transport_id,pl_connect_data.local_sctp_port,pl_connect_data.remote_sctp_port)!=-1){
// The port is already used for listen
p_sctp_req_api.raise(S_SCTP_Connect, SCTP_operation_execption:{SCTP_ENG_PORT_ALREADY_USED,"The port is already used."}) to vl_sender
// Stop further processing
return;
}
// create a new assoc entry
var integer vl_new_assoc:=SCTP_allocate_assoc_entry();
// Fill the data
v_assoc_db.associations[vl_new_assoc].assoc_data:={
state := SCTP_ASSOC_COOKIE_WAIT,
transport_id :=pl_connect_data.transport_id ,
local_port:=pl_connect_data.local_sctp_port,
remote_port:=pl_connect_data.remote_sctp_port,
own_tag:=SCTP_rnd_32bit_int(),
rem_comp:=vl_sender
}
v_assoc_db.associations[vl_new_assoc].assoc_data.own_tsn:=v_assoc_db.associations[vl_new_assoc].assoc_data.own_tag
v_assoc_db.associations[vl_new_assoc].assoc_data.reconf_own_seq_num:=v_assoc_db.associations[vl_new_assoc].assoc_data.own_tag
// process the options
if(ispresent(pl_connect_data.options)){
var integer k
for(k:=0;k<lengthof(pl_connect_data.options);k:=k+1){
if(ischosen(pl_connect_data.options[k].i_data_supported)){
v_assoc_db.associations[vl_new_assoc].assoc_data.i_data_supported:=pl_connect_data.options[k].i_data_supported
}
if(ischosen(pl_connect_data.options[k].forward_tsn_supported)){
v_assoc_db.associations[vl_new_assoc].assoc_data.forward_tsn_supported:=pl_connect_data.options[k].forward_tsn_supported
}
if(ischosen(pl_connect_data.options[k].reconfig_params)){
v_assoc_db.associations[vl_new_assoc].assoc_data.reconf_params:=pl_connect_data.options[k].reconfig_params
}
}
}
// Add the assoc to the id map
if(SCTP_add_id_map(vl_new_assoc,pl_connect_data.transport_id,pl_connect_data.local_sctp_port,pl_connect_data.remote_sctp_port)==-1){
// Something went wrong
// free the assoc entry
SCTP_free_assoc_entry(vl_new_assoc);
// Raise the exception
p_sctp_req_api.raise(S_SCTP_Listen, SCTP_operation_execption:{SCTP_ENG_INTERNAL_ERROR,"Internal error. SCTP_add_id_map -1"}) to vl_sender
// Stop further processing
return;
}
// Send the INIT
SCTP_message_send_init(vl_new_assoc)
// Start timer
v_assoc_db.associations[vl_new_assoc].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(vl_new_assoc,c_timer_T1,v_assoc_db.associations[vl_new_assoc].assoc_data.rto)
// Send reply
p_sctp_req_api.reply(S_SCTP_Connect:{?} value {vl_new_assoc}) to vl_sender
}
// Send INIT
function SCTP_message_send_init(in integer pl_assoc_id) runs on SCTP_Engine_CT {
var SCTP_Packet vl_sctp := c_empty_sctp_packet
var SCTP_Init_chunk vl_init_chunk := c_empty_init_chunk
vl_init_chunk:={
init_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tag,
a_rwnd:=65535,
os:=65535,
mis:=65535,
init_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn
}
// Indicate IDATA support if needed
if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported or v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported){
var integer vl_idx:=0
if(ispresent(vl_init_chunk.params)){
vl_idx:=lengthof(vl_init_chunk.params)
}
vl_init_chunk.params[vl_idx].supported_extensions:={
param_type := 32776,
param_length :=0,
supported_extensions:={}
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
vl_init_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_init_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=64
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported){
vl_init_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_init_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=130
}
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported){
var integer vl_idx:=0
if(ispresent(vl_init_chunk.params)){
vl_idx:=lengthof(vl_init_chunk.params)
}
vl_init_chunk.params[vl_idx].forward_tsn_supported:={
param_type := 49152,
param_length :=0
}
}
vl_sctp:={
common_header :={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port
},
chunks:={{init:=vl_init_chunk}}
}
// Send the packet
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp)
}
/*****************************************************************************/
//
// Timeout handlers functions
//
/*****************************************************************************/
// Handles the T1_init timer timeout
// The event is already removed from the event_db
function SCTP_timeout_T1_init(in integer pl_assoc_id) runs on SCTP_Engine_CT {
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
if( v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.max_init_retrans){
// We should retransmitt the INIT
// increase the counter
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter +1
// Calculate the new T1_timer value
// See 6.3 of the RFC4960
var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
)
// Send the INIT
SCTP_message_send_init(pl_assoc_id)
// Start timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T1,vl_new_timeout)
} else {
// Give up
// Notify the user
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := true,
reason := "INIT timeout. No answer received from the remote side."
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
// Set the state of the association
SCTP_assoc_set_closed(pl_assoc_id)
}
}
// Handles the T1_cookie timer timeout
// The event is already removed from the event_db
function SCTP_timeout_T1_cookie(in integer pl_assoc_id) runs on SCTP_Engine_CT {
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
if( v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.max_init_retrans){
// We should retransmitt the INIT
// increase the counter
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter +1
// Calculate the new T1_timer value
// See 6.3 of the RFC4960
var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
)
// Send the INIT
SCTP_message_send_cookie_echo(pl_assoc_id)
// Start timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T1_cookie,vl_new_timeout)
} else {
// Give up
// Notify the user
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := true,
reason := "Cookie echo timeout. No answer received from the remote side."
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(pl_assoc_id)
}
}
// Handles the T3_rtx timer timeout
// The event is already removed from the event_db
function SCTP_timeout_T3_rtx(in integer pl_assoc_id) runs on SCTP_Engine_CT {
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
if( v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans){
// increase the counter
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter +1
// we shoudl mark the first chunk for retransmission
var integer vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
if(vl_i!=-1){
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=true
// try to be more agressive with resending. Resend teh half of the out queue
var integer vl_k:= (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries-1)/2
while(vl_k>0){
vl_i:= v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].next_element
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=true
vl_k:=vl_k-1
}
} else {
// Ops we forget to stop the timer
// nothing to do
return
}
// Adjust cwnd and ssthres
v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh:=SCTP_max(v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd/2,v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu*4)
v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:=v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu
v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes:=0
// Schedule the sending of the data packets
SCTP_data_send_scheduler(pl_assoc_id)
} else {
// Give up
// Notify the user
var SCTP_Notification_data vl_notification:={
assoc_id := pl_assoc_id,
notification:= {comm_lost := {}},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := false,
reason := "Retransmit timeout. No answer received from the remote side."
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(pl_assoc_id)
}
}
// Handles the chutdown timer timeout
function SCTP_timeout_T2_shutdown(in integer pl_assoc_id) runs on SCTP_Engine_CT {
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
if( v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans){
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter +1
if(v_assoc_db.associations[pl_assoc_id].assoc_data.state==SCTP_ASSOC_SHUTDOWN_SENT){
// Resend shutdown
SCTP_message_send_shutdown(pl_assoc_id)
} else {
// Resend shutdown ack
SCTP_message_send_shutdown_ack(pl_assoc_id)
}
// calculate the new timeout
var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
)
// Schedule the resend
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T2_shutdown,vl_new_timeout)
} else {
// Give up
// Notify the user
var SCTP_Notification_data vl_notification:={
assoc_id := pl_assoc_id,
notification:= {comm_lost := {}},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := false,
reason := "Retransmit timeout. No answer received from the remote side."
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(pl_assoc_id)
}
}
function SCTP_timeout_heartbeat(in integer pl_assoc_id) runs on SCTP_Engine_CT {
v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id:=-1
if( v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans){
v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter+1
SCTP_hearbeat_schedule(pl_assoc_id)
SCTP_message_send_heartbeat(pl_assoc_id)
} else {
// Give up
// Notify the user
var SCTP_Notification_data vl_notification:={
assoc_id := pl_assoc_id,
notification:= {comm_lost := {}},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := false,
reason := "Heartbeat timeout. No answer received from the remote side."
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(pl_assoc_id)
}
}
function SCTP_timeout_close(in integer pl_assoc_id) runs on SCTP_Engine_CT {
v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer:=-1
// Notify the user
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := true,
reason := "Shutdown finished"
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(pl_assoc_id)
}
function SCTP_timeout_reconfig(in integer pl_assoc_id) runs on SCTP_Engine_CT {
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=-1
if( v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter<v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans){
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter+1
var SCTP_Packet vl_sctp := c_empty_sctp_packet
vl_sctp:={
common_header:={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
},
chunks:={{re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req}}
}
// resend the last message
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
// restart the timer
SCTP_timer_reconf_start(pl_assoc_id)
} else {
// Give up
// Notify the user
var SCTP_Notification_data vl_notification:={
assoc_id := pl_assoc_id,
notification:= {comm_lost := {}},
options := omit
}
p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
var SCTP_Shutdown_data vl_sh_data:={
assoc_id:=pl_assoc_id,
graceful_shutdown := false,
reason := "Reconfig timeout. No answer received from the remote side."
}
p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp
SCTP_assoc_set_closed(pl_assoc_id)
}
}
function SCTP_timeout_calculate_timeout_value(in float pl_rto, in integer pl_counter, in float pl_max) return float{
// Calculate the new timeout value
// See 6.3 of the RFC4960
var float vl_new_timeout:=pl_rto
for(var integer i:=1; i<=pl_counter; i:=i+1){
vl_new_timeout:=vl_new_timeout*2.0;
if(vl_new_timeout>=pl_max){
vl_new_timeout:=pl_max
break
}
}
return vl_new_timeout
}
/*****************************************************************************/
//
// Misc functions
//
/*****************************************************************************/
// generate random a 32 bit non zero integer
function SCTP_rnd_32bit_int() return integer {
return float2int(4294967295.0*rnd()) + 1
}
function SCTP_min(in integer pl_a, in integer pl_b) return integer {
if(pl_a<pl_b){
return pl_a
}
return pl_b
}
function SCTP_max(in integer pl_a, in integer pl_b) return integer {
if(pl_a>pl_b){
return pl_a
}
return pl_b
}
//return the next TSN, wrap if needed
function SCTP_next_tsn(in integer pl_tsn) return integer {
return (pl_tsn+1) mod 4294967296 // the return value is between 0 and (2^32)-1 wrapping around
}
//return the previousTSN, wrap if needed
function SCTP_prev_tsn(in integer pl_tsn) return integer {
return (pl_tsn-1) mod 4294967296 // the return value is between 0 and (2^32)-1 wrapping around
}
// Compares 2 TSNs, return -1, 0, 1
function SCTP_compare_tsn(in integer pl_tsn_a, in integer pl_tsn_b) return integer {
// See RFC 1982, SERIAL_BITS = 32
if(pl_tsn_a == pl_tsn_b){
return 0 // Equal
}
if(( (pl_tsn_a < pl_tsn_b) and ((pl_tsn_b - pl_tsn_a) > 2147483648)) or
( (pl_tsn_a > pl_tsn_b) and ((pl_tsn_a - pl_tsn_b) < 2147483648)) ){
return 1 // pl_tsn_a Greater than pl_tsn_b
}
return -1 // pl_tsn_a not equal and not greater than pl_tsn_b
}
//return the next SSN, wrap if needed
function SCTP_next_ssn(in integer pl_ssn) return integer {
return (pl_ssn+1) mod 65536 // the return value is between 0 and (2^16)-1 wrapping around
}
// Compares 2 SSNs, return -1, 0, 1
function SCTP_compare_ssn(in integer pl_ssn_a, in integer pl_ssn_b) return integer {
// See RFC 1982, SERIAL_BITS = 16
if(pl_ssn_a == pl_ssn_b){
return 0 // Equal
}
if(( (pl_ssn_a < pl_ssn_b) and ((pl_ssn_b - pl_ssn_a) < 32768)) or
( (pl_ssn_a > pl_ssn_b) and ((pl_ssn_a - pl_ssn_b) > 32768)) ){
return 1 // pl_ssn_a Greater than pl_ssn_b
}
return -1 // pl_ssn_a not equal and not greater than pl_ssn_b
}
// Encode decode float
external function SCTP_misc_float2oct(in float pl_in) return octetstring
external function SCTP_misc_oct2float(in octetstring pl_in) return float
/*****************************************************************************/
//
// int2int map handling functions
//
/*****************************************************************************/
// Allocate a new int2int map
external function SCTP_int2int_new() return integer
// Free the int2int map
external function SCTP_int2int_delete(in integer pl_map_id)
// Add/Update value in a map
external function SCTP_int2int_add(in integer pl_map_id, // The id of the map
in integer pl_key, // The key
in integer pl_value // the value stored for the key
) return integer // 0 - OK, -1 - Error, wrong map id
// Get value from a map
external function SCTP_int2int_get(in integer pl_map_id, // The id of the map
in integer pl_key, // The key
out integer pl_value // the value stored for the key
) return integer // 0 - OK, -1 - Error, wrong map id, wrong key
/*****************************************************************************/
//
// Chunk queue handling functions
//
/*****************************************************************************/
// Put an element into the end of the queue
function SCTP_Chunk_queue_push(inout SCTP_Chunk_queue_db_t pl_chunk_db, in SCTP_Chunk_queue_element_t pl_chunk){
var integer vl_idx:=-1
if(pl_chunk_db.first_free==-1){
// No free element in the list
// Alocate a new one
vl_idx:=lengthof(pl_chunk_db.chunk_queue)
} else {
vl_idx:=pl_chunk_db.first_free
// remove it from the free queue
pl_chunk_db.first_free:=pl_chunk_db.chunk_queue[vl_idx].next_element
if(pl_chunk_db.first_free==-1){
// That was the last entry on the free list
pl_chunk_db.last_free:=-1
}
}
// Set the new element
pl_chunk_db.chunk_queue[vl_idx]:=pl_chunk;
// Chain the entry into the tail to the list
pl_chunk_db.chunk_queue[vl_idx].next_element:=-1; // This will be the last entry
if(pl_chunk_db.last_element!=-1){
pl_chunk_db.chunk_queue[pl_chunk_db.last_element].next_element:=vl_idx
}
pl_chunk_db.last_element:=vl_idx
if(pl_chunk_db.first_element==-1){
// There was no first element.
pl_chunk_db.first_element:=vl_idx
}
pl_chunk_db.used_entries:=pl_chunk_db.used_entries+1;
}
// Pop the first element from the queue
function SCTP_Chunk_queue_pop(inout SCTP_Chunk_queue_db_t pl_chunk_db){
if(pl_chunk_db.used_entries>0){
// there is something to pop
// remove from the used list
var integer vl_idx:=pl_chunk_db.first_element
pl_chunk_db.first_element:=pl_chunk_db.chunk_queue[vl_idx].next_element
if(pl_chunk_db.first_element==-1){
// That was the last entry on the list
pl_chunk_db.last_element:=-1
}
// Clear the element
pl_chunk_db.chunk_queue[vl_idx]:=c_empty_Chunk_queue_element;
// put it into the end of the free list
pl_chunk_db.chunk_queue[vl_idx].next_element:=-1; // This will be the last entry
if(pl_chunk_db.last_free!=-1){
pl_chunk_db.chunk_queue[pl_chunk_db.last_free].next_element:=vl_idx
}
pl_chunk_db.last_free:=vl_idx
if(pl_chunk_db.first_free==-1){
// There was no first element.
pl_chunk_db.first_free:=vl_idx
}
pl_chunk_db.used_entries:=pl_chunk_db.used_entries-1;
}
}
// Examines the tx chunk queue and marks the packet, which should be abandoned.
// If there are packets to abandon generates the forward TSN chunk
function SCTP_data_abandon_marker(in integer pl_assoc_id, inout SCTP_Chunk_List pl_chunks_before_data) runs on SCTP_Engine_CT {
if(v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported){
// the function is not active
return
}
var boolean vl_abandoned:= false
var integer vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
var integer_list vl_list:={}
while(vl_i!=-1){
if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag){
// The chunk is marked for retransmission
// We should inspect only these chunks
if( (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].counter == 0) // reached the retransmit counter limit
or ( (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].lifetime>0.0) // reached the lifetime
and (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].lifetime<t_run_time.read)
)
){
// abandon the packet
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=false
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].unmark_flag:= true
vl_list[sizeof(vl_list)]:=vl_i
vl_abandoned:=true
}
}
}
// If new packet were market to abandon
// find and mark all of the chunk & messages belongs to the marked chunks
if(vl_abandoned){
for(var integer vl_k:=0;vl_k<sizeof(vl_list);vl_k:=vl_k+1){
var integer vl_stream_id:=SCTP_data_get_chunk_stream_id(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_list[vl_k]].chunk)
var integer vl_ssn:=SCTP_data_get_chunk_ssn(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_list[vl_k]].chunk)
// mark the message as abandoned in the stream queue
SCTP_data_abandon_msg_in_stream(vl_stream_id,vl_ssn,SCTP_message_get_U_bit(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_list[vl_k]].chunk),pl_assoc_id)
// find the releated chunks in the chunk queue
vl_i:=SCTP_data_find_related(vl_stream_id,vl_ssn,vl_list[vl_k],v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue)
while(vl_i!=-1){
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=false
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].unmark_flag:= true
vl_i:=SCTP_data_find_related(vl_stream_id,vl_ssn,vl_i,v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue)
}
}
}
// Generate the forward tsn chunk if needed
// check the beggining of the list.
var SCTP_Chunk vl_chunk
if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
vl_chunk.iforward_tsn:={
chunk_type:=194,
flags:='00000000'B,
chunk_length:=0,
new_tsn:=-1,
stream_ssn_list:={}
}
} else {
vl_chunk.forward_tsn:={
chunk_type:=192,
flags:='00000000'B,
chunk_length:=0,
new_tsn:=-1,
stream_ssn_list:={}
}
}
vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
var boolean vl_iforward:=false
// We iterate through the chunks in order
while( (vl_i != -1) and (v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].unmark_flag) ){
vl_iforward:=false
// set the tsn
if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
vl_chunk.iforward_tsn.new_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].tsn
} else {
vl_chunk.forward_tsn.new_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].tsn
}
// add the skiped ssn to the map if needed
if(SCTP_message_get_U_bit(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk)=='0'B){
// ordered chunk
// find the index of the stream in the list
var integer vl_a:=0
var integer vl_len
if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
vl_len:=lengthof(vl_chunk.iforward_tsn.stream_ssn_list)
} else {
vl_len:=lengthof(vl_chunk.forward_tsn.stream_ssn_list)
}
for(vl_a:=0;vl_a<vl_len;vl_a:=vl_a+1){
var integer vl_s
if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
vl_s:=vl_chunk.iforward_tsn.stream_ssn_list[vl_a].stream_id
} else {
vl_s:=vl_chunk.forward_tsn.stream_ssn_list[vl_a].stream_id
}
if(vl_s==SCTP_data_get_chunk_stream_id(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk)){
break
}
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported){
vl_chunk.iforward_tsn.stream_ssn_list[vl_a]:={stream_id:=SCTP_data_get_chunk_stream_id(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk),
reserved:=0,
mid:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk.idata.mid
}
} else {
vl_chunk.forward_tsn.stream_ssn_list[vl_a]:={stream_id:=SCTP_data_get_chunk_stream_id(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk),
ssn:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk.data.ssn
}
}
}
SCTP_Chunk_queue_pop(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue)
// reset the T3 timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
SCTP_timer_T3_restart(pl_assoc_id)
vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
}
// Add forward tsn to the list if built
if(vl_iforward){
pl_chunks_before_data[lengthof(pl_chunks_before_data)]:=vl_chunk
}
}
// Finf the next chunk which belongs to the given stream-id, ssn, starting the search from the given idx
function SCTP_data_find_related ( in integer pl_stream_id,
in integer pl_ssn,
in integer pl_idx,
in SCTP_Chunk_queue_element_list_t pl_chunk_queue
) runs on SCTP_Engine_CT return integer{
if(SCTP_message_get_E_bit(pl_chunk_queue[pl_idx].chunk) == '1'B){
return -1 // This is the last chunk of the message, there is no next message
}
var bitstring vl_u:=SCTP_message_get_U_bit(pl_chunk_queue[pl_idx].chunk)
pl_idx:=pl_chunk_queue[pl_idx].next_element
while(pl_idx!=-1){
if( (pl_ssn==SCTP_data_get_chunk_ssn(pl_chunk_queue[pl_idx].chunk))
and (pl_stream_id==SCTP_data_get_chunk_stream_id(pl_chunk_queue[pl_idx].chunk))
and (vl_u==SCTP_message_get_U_bit(pl_chunk_queue[pl_idx].chunk))
){
return pl_idx // found it
}
}
return -1
}
// Marks one message as abandoned in a stream queue
function SCTP_data_abandon_msg_in_stream( in integer pl_stream_id,
in integer pl_ssn,
in bitstring pl_u_bit,
in integer pl_assoc_id
) runs on SCTP_Engine_CT {
// find the idx of the stream
var integer vl_stream_idx:=-1
if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_stream_idx_map_id,
pl_stream_id,
vl_stream_idx
)!=-1){
if(pl_u_bit=='1'B){
vl_stream_idx:=vl_stream_idx+1 // we need to inspect the unordered queue
}
var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_stream_idx].messages.first_element
while(vl_idx!=-1){
var integer vl_comp:=SCTP_compare_ssn(pl_ssn, SCTP_data_get_chunk_ssn(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_stream_idx].messages.chunk_queue[vl_idx].chunk))
if(vl_comp==0){
// we found, the message
// mark it
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_stream_idx].messages.chunk_queue[vl_idx].unmark_flag:=true
return // nothing to do more
} else if(vl_comp<0){
// The ssn of the message is greater than the searhed one, nothing to do more
return
}
vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_stream_idx].messages.chunk_queue[vl_idx].next_element
}
}
}
// Assembles and send data packet
// Tries to send as many as enabled by the congestion controll mechanism
function SCTP_data_send_scheduler(in integer pl_assoc_id, in SCTP_Chunk_List pl_chunks_before_data:={}) runs on SCTP_Engine_CT {
var SCTP_Packet vl_sctp := c_empty_sctp_packet
vl_sctp:={
common_header :={
source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port,
destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port,
verification_tag := v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag
},
chunks:=pl_chunks_before_data
}
SCTP_data_abandon_marker(pl_assoc_id,vl_sctp.chunks)
var integer vl_before_list_size:=lengthof(vl_sctp.chunks)
//log("Schedule 1 ", v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd)
//log("Schedule 2 ", v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh)
//log("Schedule 3 ", v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes)
// If there is data chank marked for retransmission, we try to send them first regardless the cwnd
while(SCTP_data_add_packet(vl_sctp,pl_assoc_id)){
//log("Send")
SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp);
vl_before_list_size := 0
vl_sctp.chunks:={}
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries!=0){
SCTP_timer_T3_start(pl_assoc_id) // start the timer if data chunk is on the fly
}
//log("Schedule end ", v_assoc_db.associations[pl_assoc_id].assoc_data)
// If shuting down
if(v_assoc_db.associations[pl_assoc_id].assoc_data.state == SCTP_ASSOC_SHUTDOWN_RECEIVED){
if(SCTP_tx_queue_empty(pl_assoc_id)){ // No more data to send
// Send shutdown ack
// Stop all timers
SCTP_timer_stop_all(pl_assoc_id)
// Reset the retransmit counter
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
// Send shutdown ack
SCTP_message_send_shutdown_ack(pl_assoc_id)
// Start timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T2_shutdown,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
// Set the state
v_assoc_db.associations[pl_assoc_id].assoc_data.state := SCTP_ASSOC_SHUTDOWN_ACK_SENT
SCTP_hearbeat_stop(pl_assoc_id)
}
} else if(v_assoc_db.associations[pl_assoc_id].assoc_data.state == SCTP_ASSOC_SHUTDOWN_PENDING){
if(SCTP_tx_queue_empty(pl_assoc_id)){ // No more data to send
// Send shutdown
// Stop all timers
SCTP_timer_stop_all(pl_assoc_id)
// Reset the retransmit counter
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
// Send shutdown ack
SCTP_message_send_shutdown(pl_assoc_id)
// Start timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T2_shutdown,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
// Set the state
v_assoc_db.associations[pl_assoc_id].assoc_data.state := SCTP_ASSOC_SHUTDOWN_SENT
SCTP_hearbeat_stop(pl_assoc_id)
}
}
}
// Return true if all of the sending queues are empty
function SCTP_tx_queue_empty(in integer pl_assoc_id) runs on SCTP_Engine_CT return boolean {
if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries!=0){
return false // There are unacknowledged chunk
}
// check the stream queues
for(var integer vl_idx:=0;vl_idx<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues);vl_idx:=vl_idx+1){
if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_idx].messages.used_entries!=0){
return false // There are data to send
}
}
return true
}
// Adds as many data packets to the message as many possible
// Limits:
// - pmtu
// - congestion control
// - Packet size
function SCTP_data_add_packet(inout SCTP_Packet vl_sctp, in integer pl_assoc_id) runs on SCTP_Engine_CT return boolean {
var integer vl_packet_size:= 12 // the size of the common header
if(lengthof(vl_sctp.chunks)!=0){
// Calculate the actual packet size
var octetstring vl_tmp:=f_SCTP_enc(vl_sctp);
vl_packet_size:=lengthof(vl_tmp);
}
// We are allowed to send that many bytes now
var integer max_to_send:=v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd-v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size;
// calculate the maximum packet size
var integer rem_packet_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu-vl_packet_size;
// look for packet to retransmit
if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries>0){
// the tx chunk queue is not empty
var integer vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element
while(vl_i!=-1){
if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag){
// the chunk is marked for retransmit
var integer vl_chunk_size:=SCTP_data_get_chunk_size(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk)
if(rem_packet_size>=vl_chunk_size){
// add the chunk
vl_sctp.chunks[sizeof(vl_sctp.chunks)]:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].chunk
rem_packet_size:=rem_packet_size-vl_chunk_size
// Adjust the retransmit counter of the packet
if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].counter>0){
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].counter:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].counter-1
}
// clear mark
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].mark_flag:=false
} else {
// chunk doesn't fit into the packet
return sizeof(vl_sctp.chunks)>0 // there is something to send
}
}
vl_i:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_i].next_element
}
}
// Adjust the max_to_send according to the peer receive window
if(max_to_send>v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd){
if((v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd==0) and // The remote side closed the window
(v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size == 0) // and not data in fligth
){ // we can try to send 1 data packet. Zero window probe
max_to_send:= SCTP_min(max_to_send,v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu) // max one MTU is allowed, if the cwnd allows it.
} else {
max_to_send:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd // limit the max data
}
}
// new data sending allowed
// 6.1 rule B
var integer vl_i:=0
while((vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues)) and (max_to_send>c_min_chunk_size ) and (rem_packet_size>c_min_chunk_size )){
// iterate through the queues for message to send
var integer vl_msg_q_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.scheduled_queue
var boolean vl_can_try_next := true
// The size of the chunk is limited by the
// - rem_packet_size
// - v_assoc_db.associations[pl_assoc_id].assoc_data.data_chunk_max_size
var integer vl_max_chunk_size:=SCTP_min(rem_packet_size,v_assoc_db.associations[pl_assoc_id].assoc_data.data_chunk_max_size)
if((v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_msg_q_idx].messages.used_entries>0)
and (not v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_msg_q_idx].flag)){
var integer vl_chunk_size:=SCTP_data_add_one_chunk(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_msg_q_idx] , // The msg queue of the stream
v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue, // The tx chunk queue
vl_sctp, // The SCTP message, will be sent
pl_assoc_id, // The association
vl_max_chunk_size, // The max size of the chunk
vl_can_try_next // true - last chunk of the message is added, false otherwise
)
rem_packet_size:=rem_packet_size-vl_chunk_size
max_to_send:=max_to_send-vl_chunk_size
}
if(v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd==0){ // We added the zero window probe
return sizeof(vl_sctp.chunks)>0 // Should not add more chunk
}
// next queue
if(vl_can_try_next){
v_assoc_db.associations[pl_assoc_id].assoc_data.scheduled_queue:= (vl_msg_q_idx+1) mod lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues)
vl_i:=vl_i+1
}
}
if(vl_i==lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues)){
// no more dtat to send
v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes:=0
}
return sizeof(vl_sctp.chunks)>0
}
// adds 1 chunk to the SCTP msg for sending
// return the size of the added chunk
// or 0 if not chunk added
// indicates if the last chunk of the message were added
function SCTP_data_add_one_chunk( inout SCTP_msg_queue_t pl_stream_tx_queue, // The msg queue of the stream
inout SCTP_Chunk_queue_db_t pl_tx_chunk_queue, // The tx chunk queue
inout SCTP_Packet pl_sctp, // The SCTP message, will be sent
in integer pl_assoc_id, // The association
in integer pl_max_size, // The max size of the chunk
out boolean pl_last_chunk // true - last chunk of the message is added, false otherwise
) runs on SCTP_Engine_CT return integer {
var integer vl_queue_entry:=pl_stream_tx_queue.messages.first_element
var integer vl_chunk_size:=SCTP_data_get_chunk_size(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
var integer vl_chunk_header_size:=SCTP_data_get_chunk_header_size(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
var integer ret_val:=0
pl_last_chunk := false
if(pl_max_size<c_min_chunk_size){
return ret_val // Can't add more chunk
}
if( ((pl_stream_tx_queue.num_of_queued_octets==0)
and (pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].lifetime>=0.0)
and (pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].lifetime<t_run_time.read)) // lifetime expired
or
(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].unmark_flag) //marked for abandon
){
// drop the message
SCTP_Chunk_queue_pop(pl_stream_tx_queue.messages)
pl_stream_tx_queue.num_of_queued_octets:=0
return ret_val // Can't add more chunk
}
if( (pl_stream_tx_queue.num_of_queued_octets==0) and
( vl_chunk_size <= pl_max_size)
){
// The whole message is fit into the chunk
// set counters
v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+vl_chunk_size
// set the tsn
var integer vl_act_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn
v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=SCTP_next_tsn(vl_act_tsn)
pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].tsn:=vl_act_tsn
SCTP_data_set_chunk_tsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk,vl_act_tsn)
// Whole message in one chunk, set B & E bits
SCTP_message_set_B_bit('1'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
SCTP_message_set_E_bit('1'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
// copy the packet to the tx_queue
SCTP_Chunk_queue_push(pl_tx_chunk_queue,
pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry]
)
// add the chunk to the message
pl_sctp.chunks[sizeof(pl_sctp.chunks)]:=pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk
// remove it from the stream queue
SCTP_Chunk_queue_pop(pl_stream_tx_queue.messages)
pl_last_chunk := true // Last chunk
ret_val := vl_chunk_size // report the chunk size
} else {
// The message doesn't fit into one chunk, or fragmented
if(pl_stream_tx_queue.num_of_queued_octets==0){
// It should be fragmented
// Generate the first fragment
// set the tsn
var integer vl_act_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn
v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=SCTP_next_tsn(vl_act_tsn)
pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].tsn:=vl_act_tsn
SCTP_data_set_chunk_tsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk,vl_act_tsn)
// set B & E bits
SCTP_message_set_B_bit('1'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
SCTP_message_set_E_bit('0'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
// copy the packet to the tx_queue
SCTP_Chunk_queue_push(pl_tx_chunk_queue,
pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry]
)
// Set the next Fragment Sequence Number if I-DATA
SCTP_set_next_fsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
// set the data part of the packet
var integer vl_chunk_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.last_element
// The whole message was copied, remove the unnessary data. Note: octetstring uses copy-on-write
SCTP_message_substr_chunk_data(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_chunk_idx].chunk,0,pl_max_size-vl_chunk_header_size);
// add the chunk to the message
pl_sctp.chunks[sizeof(pl_sctp.chunks)]:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_chunk_idx].chunk
// set counters
v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+pl_max_size
pl_stream_tx_queue.num_of_queued_octets:=pl_max_size-vl_chunk_header_size
pl_last_chunk := false // not the last chunk
ret_val := pl_max_size // report th echunk size
} else {
// already fragmented, next fragment
// It should be fragmented
// set the tsn
var integer vl_act_tsn:=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn
v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=SCTP_next_tsn(vl_act_tsn)
pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].tsn:=vl_act_tsn
SCTP_data_set_chunk_tsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk,vl_act_tsn)
// set B & E bits
SCTP_message_set_B_bit('0'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
if((vl_chunk_size-pl_stream_tx_queue.num_of_queued_octets)<=pl_max_size){
// Last chunk of the message
SCTP_message_set_E_bit('1'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
ret_val:=vl_chunk_size-pl_stream_tx_queue.num_of_queued_octets
pl_last_chunk := true // not the last chunk
} else {
// middle chunk
SCTP_message_set_E_bit('0'B, pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
ret_val:=pl_max_size
pl_last_chunk := false // not the last chunk
}
// copy the packet to the tx_queue
SCTP_Chunk_queue_push(pl_tx_chunk_queue,
pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry]
)
// Set the next Fragment Sequence Number if I-DATA
SCTP_set_next_fsn(pl_stream_tx_queue.messages.chunk_queue[vl_queue_entry].chunk)
// The whole message was copied, remove the unnessary data. Note: octetstring uses copy-on-write
var integer vl_chunk_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.last_element
SCTP_message_substr_chunk_data(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_chunk_idx].chunk,pl_stream_tx_queue.num_of_queued_octets,ret_val-vl_chunk_header_size);
// add the chunk to the message
pl_sctp.chunks[sizeof(pl_sctp.chunks)]:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_chunk_idx].chunk
// set counters
v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+ret_val
if(pl_last_chunk){
// remove it from the stream queue
SCTP_Chunk_queue_pop(pl_stream_tx_queue.messages)
pl_stream_tx_queue.num_of_queued_octets:=0
} else {
pl_stream_tx_queue.num_of_queued_octets:=pl_stream_tx_queue.num_of_queued_octets+ret_val-vl_chunk_header_size
// middle chunk, we used all of the spaces in the packet
}
}
}
return ret_val
}
// Set the next Fragment Sequence Number if I-DATA
function SCTP_set_next_fsn(inout SCTP_Chunk pl_chunk){
if(ischosen(pl_chunk.idata)){
// I-DATA chunk
if(pl_chunk.idata.B_flag=='1'B){
// Beginning chunk, the next fsn will be 1
pl_chunk.idata.ppid_fsn:=1
} else {
pl_chunk.idata.ppid_fsn:=SCTP_next_tsn(pl_chunk.idata.ppid_fsn) // +1 mod 2^32 like TSN
}
}
}
function SCTP_message_substr_chunk_data(inout SCTP_Chunk pl_chunk, in integer pl_begin, in integer pl_length){
if(ischosen(pl_chunk.data)){
// Data chunk
pl_chunk.data.user_data:=substr(pl_chunk.data.user_data,pl_begin,pl_length)
} else {
// I-DATA chunk
pl_chunk.idata.user_data:=substr(pl_chunk.idata.user_data,pl_begin,pl_length)
}
}
function SCTP_message_set_B_bit(in bitstring pl_B_bit, inout SCTP_Chunk pl_chunk){
if(ischosen(pl_chunk.data)){
// Data chunk
pl_chunk.data.B_flag:=pl_B_bit
} else {
// I-DATA chunk
pl_chunk.idata.B_flag:=pl_B_bit
}
}
function SCTP_message_get_B_bit(in SCTP_Chunk pl_chunk) return bitstring {
if(ischosen(pl_chunk.data)){
// Data chunk
return pl_chunk.data.B_flag
} else {
// I-DATA chunk
return pl_chunk.idata.B_flag
}
return '0'B
}
function SCTP_message_set_E_bit(in bitstring pl_E_bit, inout SCTP_Chunk pl_chunk){
if(ischosen(pl_chunk.data)){
// Data chunk
pl_chunk.data.E_flag:=pl_E_bit
} else {
// I-DATA chunk
pl_chunk.idata.E_flag:=pl_E_bit
}
}
function SCTP_message_get_E_bit(in SCTP_Chunk pl_chunk) return bitstring {
if(ischosen(pl_chunk.data)){
// Data chunk
return pl_chunk.data.E_flag
} else {
// I-DATA chunk
return pl_chunk.idata.E_flag
}
return '0'B
}
function SCTP_message_get_U_bit(in SCTP_Chunk pl_chunk) return bitstring {
if(ischosen(pl_chunk.data)){
// Data chunk
return pl_chunk.data.U_flag
} else {
// I-DATA chunk
return pl_chunk.idata.U_flag
}
return '0'B
}
function SCTP_data_get_chunk_size(in SCTP_Chunk pl_chunk) return integer {
var integer vl_chunk_size:=0;
if(ischosen(pl_chunk.data)){
// Data chunk
vl_chunk_size:=16+lengthof(pl_chunk.data.user_data)
} else {
// I-DATA chunk
vl_chunk_size:=20+lengthof(pl_chunk.idata.user_data)
}
return vl_chunk_size
}
function SCTP_data_get_chunk_header_size(in SCTP_Chunk pl_chunk) return integer {
var integer vl_chunk_size:=0;
if(ischosen(pl_chunk.data)){
// Data chunk
vl_chunk_size:=16
} else {
// I-DATA chunk
vl_chunk_size:=20
}
return vl_chunk_size
}
function SCTP_data_set_chunk_tsn(inout SCTP_Chunk pl_chunk, in integer pl_tsn){
if(ischosen(pl_chunk.data)){
// Data chunk
pl_chunk.data.tsn:=pl_tsn
} else {
// I-DATA chunk
pl_chunk.idata.tsn:=pl_tsn
}
}
function SCTP_data_get_chunk_tsn(inout SCTP_Chunk pl_chunk) return integer{
if(ischosen(pl_chunk.data)){
// Data chunk
return pl_chunk.data.tsn
} else {
// I-DATA chunk
return pl_chunk.idata.tsn
}
return -1
}
function SCTP_data_get_chunk_ssn(inout SCTP_Chunk pl_chunk) return integer{
if(ischosen(pl_chunk.data)){
// Data chunk
return pl_chunk.data.ssn
} else {
// I-DATA chunk
return pl_chunk.idata.mid mod 65536
}
return -1
}
function SCTP_data_get_chunk_stream_id(inout SCTP_Chunk pl_chunk) return integer{
if(ischosen(pl_chunk.data)){
// Data chunk
return pl_chunk.data.stream_id
} else {
// I-DATA chunk
return pl_chunk.idata.stream_id
}
return -1
}
// returns the sequence number used for reassembly
// It is TSN for normal data packet
// It is the FSN for I-data packet
function SCTP_data_get_chunk_reassembly_seq_no(inout SCTP_Chunk pl_chunk) return integer{
if(ischosen(pl_chunk.data)){
// Data chunk
return pl_chunk.data.tsn
} else {
// I-DATA chunk
if(pl_chunk.idata.B_flag == '0'B){
// Not the first chunk of the message
return pl_chunk.idata.ppid_fsn
} else {
// First chunk of the message, the FSN is 0
return 0
}
}
return -1
}
function SCTP_data_chunk_append_data(inout SCTP_Chunk pl_chunk_a, in SCTP_Chunk pl_chunk_b){
if(ischosen(pl_chunk_a.data)){
pl_chunk_a.data.user_data:=pl_chunk_a.data.user_data & pl_chunk_b.data.user_data
// Data chunk
} else {
// I-DATA chunk
pl_chunk_a.idata.user_data:=pl_chunk_a.idata.user_data & pl_chunk_b.idata.user_data
}
}
function SCTP_data_chunk_copy_tsn(inout SCTP_Chunk pl_chunk_a, in SCTP_Chunk pl_chunk_b){
if(ischosen(pl_chunk_a.data)){
pl_chunk_a.data.tsn:=pl_chunk_b.data.tsn
// Data chunk
} else {
// I-DATA chunk
pl_chunk_a.idata.tsn:=pl_chunk_b.idata.tsn
}
}
function SCTP_data_get_msg_data(in SCTP_Chunk pl_chunk,inout SCTP_MSG_data pl_msg_data, in integer pl_assoc_id) {// Copy the msg
if(ischosen(pl_chunk.data)){
// Data chunk
pl_msg_data:={
assoc_id:=pl_assoc_id,
ppid:=pl_chunk.data.ppid,
stream_id:=pl_chunk.data.stream_id,
data:=pl_chunk.data.user_data,
options:=omit
}
} else {
// I-DATA chunk
pl_msg_data:={
assoc_id:=pl_assoc_id,
ppid:=pl_chunk.idata.ppid_fsn,
stream_id:=pl_chunk.idata.stream_id,
data:=pl_chunk.idata.user_data,
options:=omit
}
}
}
// Start reconf timer if needed
function SCTP_timer_reconf_start(in integer pl_assoc_id) runs on SCTP_Engine_CT{
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id!=-1){
// the timer is already running.
// Nothing to do
return
}
// Calculate the timeout
var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter,
v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
)
//start the timer
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=
SCTP_event_schedule(pl_assoc_id,c_timer_reconfig,vl_new_timeout)
}
function SCTP_timer_reconf_stop(in integer pl_assoc_id) runs on SCTP_Engine_CT{
if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id)
v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=-1
}
}
//Start the T3-rtx timer if needed
function SCTP_timer_T3_start(in integer pl_assoc_id) runs on SCTP_Engine_CT{
if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
// the timer is already running.
// Nothing to do
return
}
// Calculate the timeout
var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
)
//start the timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T3_rtx,vl_new_timeout)
}
// Reschedule the T3-rtx timer
function SCTP_timer_T3_restart(in integer pl_assoc_id) runs on SCTP_Engine_CT{
// Stop th etimer if running
if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
}
// Start it if there is not acked data
if(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.used_entries>0){
// Calculate the timeout
var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter,
v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
)
//start the timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T3_rtx,vl_new_timeout)
}
}
function SCTP_timer_T2_restart(in integer pl_assoc_id) runs on SCTP_Engine_CT{
// Stop the timer if running
if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event)
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1
}
// Clear counter
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0
//start the timer
v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=
SCTP_event_schedule(pl_assoc_id,c_timer_T2_shutdown,v_assoc_db.associations[pl_assoc_id].assoc_data.rto)
}
// Initializes the congestion control parameters
function SCTP_congestion_set_init_params(in integer pl_assoc_id) runs on SCTP_Engine_CT{
// initial cwn = min(4* pmtu, max(2*PMTU,4380)
var integer vl_cwnd:=4380
var integer vl_pmtu:=v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu
if((2*vl_pmtu)>vl_cwnd) {
vl_cwnd:=2*vl_pmtu
}
if((4*vl_pmtu)<vl_cwnd){
vl_cwnd:=4*vl_pmtu
}
v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:=vl_cwnd
v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd
}
// Start the heartbeat service
function SCTP_hearbeat_start(in integer pl_assoc_id) runs on SCTP_Engine_CT{
v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=0
SCTP_hearbeat_schedule(pl_assoc_id)
}
// Stop the heartbeat service
function SCTP_hearbeat_stop(in integer pl_assoc_id) runs on SCTP_Engine_CT{
if(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id)
}
v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=0
}
// Schedule the next heartbeat
function SCTP_hearbeat_schedule(in integer pl_assoc_id) runs on SCTP_Engine_CT{
if(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id!=-1){
SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id)
}
var float vl_new_timeout:= SCTP_timeout_calculate_timeout_value(v_assoc_db.associations[pl_assoc_id].assoc_data.rto,
v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter,
v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.rto_max
) + v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.hb_interval
v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id:=SCTP_event_schedule(pl_assoc_id,c_timer_heartbeat,vl_new_timeout)
}
}