| /////////////////////////////////////////////////////////////////////////////// |
| // Copyright (c) 2000-2019 Ericsson Telecom AB |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v2.0 |
| // which accompanies this distribution, and is available at |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // File: SCTP_Engine_Functions.ttcn |
| // Description: Functions of the SCTP Engine |
| // Rev: <RnXnn> |
| // Prodnr: CNL 113 840 |
| // |
| module SCTP_Engine_Functions{ |
| |
| import from SCTP_Engine_Definition all; |
| import from SCTP_Engine_PortTypes all; |
| import from SCTP_Types all |
| import from SCTP_Engine_Templates all |
| |
| // Allocates an entry in the assoc DB, returns the index of the entry |
| |
| function SCTP_allocate_assoc_entry() runs on SCTP_Engine_CT return integer { |
| var integer vl_selected_entry:=-1; |
| |
| if(v_assoc_db.used_entries<sizeof(v_assoc_db.associations)){ |
| // There are free entries in the list |
| // remove it from the free list |
| vl_selected_entry:=v_assoc_db.first_free |
| v_assoc_db.first_free:=v_assoc_db.associations[vl_selected_entry].next_free |
| if(v_assoc_db.first_free == -1) { |
| // the last free entry was selected |
| v_assoc_db.last_free :=-1 |
| } |
| v_assoc_db.associations[vl_selected_entry].next_free:=-1 |
| } else { |
| // No free entry, add one to the list |
| vl_selected_entry:=sizeof(v_assoc_db.associations) |
| v_assoc_db.associations[vl_selected_entry] := c_empty_SCTP_Assoc_entry |
| v_assoc_db.used_entries:=v_assoc_db.used_entries+1 |
| } |
| |
| // initialize the data |
| v_assoc_db.associations[vl_selected_entry].assoc_data:=c_empty_SCTP_Assoc_data |
| |
| // allocate int2int maps |
| |
| v_assoc_db.associations[vl_selected_entry].assoc_data.tx_stream_idx_map_id := SCTP_int2int_new() |
| v_assoc_db.associations[vl_selected_entry].assoc_data.rx_stream_idx_map_id := SCTP_int2int_new() |
| return vl_selected_entry |
| |
| } |
| |
| // Put the association into closed state |
| function SCTP_assoc_set_closed(in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| // Stop all timers |
| SCTP_timer_stop_all(pl_assoc_id) |
| |
| // Remove the association from the map |
| if( SCTP_del_id_map(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id , |
| v_assoc_db.associations[pl_assoc_id].assoc_data.local_port , |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port ) == -1) { |
| // Something went wrong |
| log("INTERNAL ERROR: SCTP_timeout_T1_init SCTP_del_id_map: the association is not found in the id map ",pl_assoc_id , " " , v_assoc_db.associations[pl_assoc_id]) |
| } |
| // Set the state of the association |
| v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_CLOSED |
| } |
| |
| // Constructs the assoc entry from Cookie echo message |
| // |
| // Return value: |
| // - -1 error |
| // - assoc_id |
| function SCTP_assoc_construct_from_cookie_echo(in SCTP_Packet pl_sctp_packet, |
| in integer pl_transport_id := -1, |
| in integer pl_parent_id := -1 |
| ) runs on SCTP_Engine_CT return integer{ |
| // first check the cookie validity |
| var integer pl_check_res :=-1 |
| if( ( lengthof(pl_sctp_packet.chunks) >0 ) and ischosen(pl_sctp_packet.chunks[0].cookie_echo)){ |
| // Seems to be a cookie echo message |
| // Check the cookie |
| pl_check_res:=SCTP_check_state_cookie(pl_sctp_packet.chunks[0].cookie_echo.cookie,v_secret_key); |
| if(pl_check_res == -1 ){ |
| // invalid MAC |
| return -1; |
| } |
| // The cookie is either valid, or expired. |
| // Construct the assoc db entry. The caller will check for expired state |
| } else { |
| // Not a valid cookie echo |
| return -1; |
| } |
| |
| // Allocate the new assoc data entry |
| var integer vl_new_assoc:=SCTP_allocate_assoc_entry(); |
| |
| // Copy the data from the parent if exists |
| if( pl_parent_id != -1) { |
| v_assoc_db.associations[vl_new_assoc]:=v_assoc_db.associations[pl_parent_id] |
| } |
| |
| v_assoc_db.associations[vl_new_assoc].assoc_data:={ |
| transport_id:=pl_transport_id, |
| local_port:=pl_sctp_packet.common_header.destination_port, |
| remote_port:=pl_sctp_packet.common_header.source_port, |
| cookie_validity:=pl_check_res |
| } |
| |
| // extract data from state cookie |
| SCTP_cookie_extract(vl_new_assoc,pl_sctp_packet.chunks[0].cookie_echo.cookie); |
| |
| return vl_new_assoc; |
| } |
| |
| // Deallocates an entry in the assoc DB. |
| function SCTP_free_assoc_entry(in integer pl_idx) runs on SCTP_Engine_CT { |
| if( (pl_idx<0) or (pl_idx>sizeof(v_assoc_db.associations)) |
| or (not ispresent(v_assoc_db.associations[pl_idx].assoc_data)) ){ |
| return // invalid index or item is already free, nothing to do |
| } |
| |
| // delete the int2int maps |
| SCTP_int2int_delete(v_assoc_db.associations[pl_idx].assoc_data.tx_stream_idx_map_id) |
| SCTP_int2int_delete(v_assoc_db.associations[pl_idx].assoc_data.rx_stream_idx_map_id) |
| |
| // Stop all timers |
| SCTP_timer_stop_all(pl_idx) |
| |
| // release all the data |
| v_assoc_db.associations[pl_idx].assoc_data:=omit |
| |
| // This entry will be the last free entry |
| v_assoc_db.associations[pl_idx].next_free := -1 |
| |
| // put the entry to the end of the chain |
| if(v_assoc_db.last_free!=-1){ |
| // There was valid last free entry |
| v_assoc_db.associations[v_assoc_db.last_free].next_free:=pl_idx |
| } |
| |
| // Move the last_free pointer |
| v_assoc_db.last_free:=pl_idx |
| |
| v_assoc_db.used_entries:=v_assoc_db.used_entries-1 |
| |
| return // We're done |
| } |
| |
| function SCTP_timer_stop_all(in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){ |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1 |
| } |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id!=-1){ |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.hb_event_id:=-1 |
| } |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer!=-1){ |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer:=-1 |
| } |
| } |
| |
| // Function to handle assoc-id <-> (transport_id,local_port,remote_port) mapping |
| // Register entries into the map |
| // return value: 0 - OK, -1 - Error |
| external function SCTP_add_id_map(in integer pl_assoc_id, |
| in integer pl_transport_id, |
| in integer pl_local_port, |
| in integer pl_remote_port) return integer |
| |
| // Delete entries from the map |
| // return value: 0 - OK, -1 - Error |
| external function SCTP_del_id_map(in integer pl_transport_id, |
| in integer pl_local_port, |
| in integer pl_remote_port) return integer |
| |
| // Search in the map |
| // return value: assoc_id - OK, -1 - Error |
| external function SCTP_find_id_map(in integer pl_transport_id, |
| in integer pl_local_port, |
| in integer pl_remote_port) return integer |
| |
| |
| // State cookie handling functions |
| // The information should be stored in state cookie |
| // timestamp & lifespan |
| // Own & remote verification tag |
| // Own & remote tie tag |
| // remote initial TSN, our own is equal to verification tag |
| // output & max input stream of remote |
| // remote advertised receive window |
| // Those are needed to restore the association data |
| // Our state cookie structure: |
| |
| // The order and size of the fields: |
| // unix_timestamp 4 octets |
| // pl_lifespan 2 |
| // pl_own_init_tag 4 |
| // pl_remote_init_tag 4 |
| // pl_tie_tag 4 |
| // pl_remote_tsn 4 |
| // pl_remote_os 2 |
| // pl_remote_mis 2 |
| // pl_remote_a_rwnd 4 |
| // flags 1 |
| // MAC 17 |
| // Summ:47 |
| // generate the state cookie. The actual time is queried. |
| external function SCTP_gen_state_cookie(in integer pl_lifespan, |
| in integer pl_own_init_tag, |
| in integer pl_remote_init_tag, |
| in integer pl_tie_tag, |
| in integer pl_remote_tsn, |
| in integer pl_remote_os, |
| in integer pl_remote_mis, |
| in integer pl_remote_a_rwnd, |
| in boolean pl_idata_flag, |
| in boolean pl_reconf_flag, |
| in boolean pl_forward_tsn_flag, |
| in octetstring pl_key |
| ) return octetstring |
| |
| // Check the received state cookie validity |
| // return values: |
| // 0: OK |
| // -1: MAC failed/ invalid contents |
| // 1: expired |
| external function SCTP_check_state_cookie(in octetstring pl_cookie, |
| in octetstring pl_key) return integer |
| |
| |
| // extract the data from the cookie into the assoc db entry |
| function SCTP_cookie_extract(in integer pl_assoc_id, |
| in octetstring pl_cookie) runs on SCTP_Engine_CT { |
| |
| v_assoc_db.associations[pl_assoc_id].assoc_data:={ |
| own_tag:=oct2int(substr(pl_cookie,6,4)), |
| remote_tag:=oct2int(substr(pl_cookie,10,4)), |
| remote_tsn:=oct2int(substr(pl_cookie,18,4)), |
| own_tsn:=oct2int(substr(pl_cookie,6,4)), |
| remote_os:=oct2int(substr(pl_cookie,22,2)), |
| remote_mis := oct2int(substr(pl_cookie,24,2)), |
| remote_a_rwnd := oct2int(substr(pl_cookie,26,4)), |
| i_data_supported := substr(oct2bit(substr(pl_cookie,30,1)),0,1) == '1'B, |
| reconf_own_seq_num := oct2int(substr(pl_cookie,6,4)), |
| reconf_remote_seq_num := oct2int(substr(pl_cookie,18,4)) |
| } |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported:= substr(oct2bit(substr(pl_cookie,30,1)),1,1) == '1'B |
| v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported:= substr(oct2bit(substr(pl_cookie,30,1)),2,1) == '1'B |
| |
| } |
| /*****************************************************************************/ |
| // |
| // Event scheduler functions |
| // |
| /*****************************************************************************/ |
| |
| // Add event to the event-time map |
| // return value: 0 - OK, -1 - Error |
| external function SCTP_event_add_map(in integer pl_event_id, |
| in float pl_event_time) |
| |
| |
| // Remove event from the map |
| // return value: 0 - OK, -1 - Error |
| external function SCTP_event_del_map(in integer pl_event_id, |
| in float pl_event_time) |
| |
| // Get the first event idx |
| // return value: event_idx - OK, -1 - Error |
| external function SCTP_event_get_map() return integer |
| |
| // Schedule the event |
| // returns the idx of the |
| function SCTP_event_schedule(in integer pl_assoc_id, |
| in integer pl_timer_id, |
| in float pl_timeout // relative timeout value |
| ) runs on SCTP_Engine_CT return integer { |
| |
| var float curr_time:=t_run_time.read |
| var integer vl_selected_entry:=-1; |
| // log(v_event_db) |
| if(v_event_db.used_entries<sizeof(v_event_db.events)){ |
| // There are free entries in the list |
| // remove it from the free list |
| vl_selected_entry:=v_event_db.first_free |
| v_event_db.first_free:=v_event_db.events[vl_selected_entry].next_free |
| if(v_event_db.first_free == -1) { |
| // the last free entry was selected |
| v_event_db.last_free :=-1 |
| } |
| v_event_db.events[vl_selected_entry].next_free:=-1 |
| } else { |
| // No free entry, add one to the list |
| vl_selected_entry:=sizeof(v_event_db.events) |
| } |
| v_event_db.used_entries:=v_event_db.used_entries+1; |
| |
| // initialize the data |
| v_event_db.events[vl_selected_entry] := { |
| next_free:=-1, |
| assoc_id:=pl_assoc_id, |
| timer_id:=pl_timer_id, |
| timeout_time:=curr_time+pl_timeout // absulute timeout val |
| } |
| |
| // insert the event into the map |
| SCTP_event_add_map(vl_selected_entry,v_event_db.events[vl_selected_entry].timeout_time) |
| |
| // rescedule the timeout timer |
| SCTP_event_recalculate_timer() |
| |
| return vl_selected_entry |
| } |
| |
| // Remove the event from the list |
| function SCTP_event_remove(in integer pl_idx) runs on SCTP_Engine_CT { |
| if( (pl_idx<0) or (pl_idx>sizeof(v_event_db.events)) |
| or (v_event_db.events[pl_idx].assoc_id==-1) ){ |
| return // invalid index or item is already free, nothing to do |
| } |
| |
| // remove the event from the map & reschedule the timer |
| SCTP_event_del_map(pl_idx,v_event_db.events[pl_idx].timeout_time) |
| SCTP_event_recalculate_timer() |
| |
| // Free the enry and move to the end of the free chain |
| v_event_db.events[pl_idx]:=c_empty_event_entry |
| |
| // put the entry to the end of the chain |
| if(v_event_db.last_free!=-1){ |
| // There was valid last free entry |
| v_event_db.events[v_event_db.last_free].next_free:=pl_idx |
| } |
| |
| // Set the first free pointer if needed |
| if(v_event_db.first_free==-1){ |
| // There was valid last free entry |
| v_event_db.first_free:=pl_idx |
| } |
| // Move the last_free pointer |
| v_event_db.last_free:=pl_idx |
| |
| v_event_db.used_entries:=v_event_db.used_entries-1; |
| |
| } |
| |
| // Set the t_next_event to the next event. Reset the timer if needed |
| function SCTP_event_recalculate_timer() runs on SCTP_Engine_CT { |
| var integer first_event:=SCTP_event_get_map(); |
| |
| if(first_event==-1){ |
| // No event in the queue |
| v_event_db.the_sceduled_event:=-1; // No scheduled event |
| if(t_next_event.running) { |
| t_next_event.stop // deactivate the timer |
| } |
| return // we're ready |
| } |
| |
| if(first_event==v_event_db.the_sceduled_event){ |
| // the first event is the already scheduled, nothing to do |
| return |
| } |
| |
| // Reschedule the timer |
| if(t_next_event.running) { |
| t_next_event.stop // stop it first |
| } |
| |
| var float curr_time:=t_run_time.read |
| |
| // Calculate the timeout value |
| var float schedule_time:=v_event_db.events[first_event].timeout_time-curr_time |
| if(schedule_time<0.0){ |
| // do not chedule in the past |
| schedule_time:=0.0 |
| } |
| |
| // remeber the event idx |
| v_event_db.the_sceduled_event:=first_event |
| |
| // Start the timer |
| t_next_event.start(schedule_time) |
| |
| return |
| |
| } |
| |
| /*****************************************************************************/ |
| // |
| // Message handler functions |
| // |
| /*****************************************************************************/ |
| |
| // Process the incoming sctp packet |
| // The INIT & COOKIE ECHO processed by other function |
| function SCTP_message_incoming_handler(in integer pl_assoc_id, |
| in SCTP_Packet pl_sctp_packet, |
| in boolean pl_cookie_preprocessed := false // if true, the Cookie Echo was preprocessed, only Cookie Ack is needed |
| ) runs on SCTP_Engine_CT { |
| // Check the verification tag |
| if(pl_sctp_packet.common_header.verification_tag!=v_assoc_db.associations[pl_assoc_id].assoc_data.own_tag){ |
| // Not for me |
| // Drop it |
| log("Verification tag mismatch. Packet dropped. ",pl_sctp_packet) |
| return; |
| } |
| |
| // trigger the processing of the data queue |
| // If new chunk has been put into the incoming chunk queue, set it true to trigger the processing of the queue |
| var boolean vl_process_data:=false |
| |
| // The answer packet |
| var SCTP_Packet vl_response := c_empty_sctp_packet |
| |
| for(var integer vl_k:=0;vl_k<lengthof(pl_sctp_packet.chunks);vl_k:=vl_k+1){ |
| // Process the chunks by calling the handler functions |
| // Each handler returns the processing result |
| // -1: Stop the further processing |
| // 0: Continue with the next chunk |
| // 1: Continue with the next chunk, postprocess the data queues |
| var integer vl_handler_ret:=-1 |
| if(ischosen(pl_sctp_packet.chunks[vl_k].init_ack)){ |
| vl_handler_ret:=SCTP_message_incoming_init_ack_handler(pl_sctp_packet.chunks[vl_k].init_ack,pl_assoc_id,vl_k) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].cookie_ack)){ |
| vl_handler_ret:=SCTP_message_incoming_cookie_ack_handler(pl_sctp_packet.chunks[vl_k].cookie_ack,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].cookie_echo)){ //Process cookie echo |
| vl_handler_ret:=SCTP_message_incoming_cookie_echo_handler(pl_sctp_packet.chunks[vl_k].cookie_echo,pl_assoc_id,vl_response,pl_cookie_preprocessed) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].data) or ischosen(pl_sctp_packet.chunks[vl_k].idata)){ // process DATA |
| vl_handler_ret:=SCTP_message_incoming_data_handler(pl_sctp_packet.chunks[vl_k],pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].sack)){ // process SACK |
| vl_handler_ret:=SCTP_message_incoming_sack_handler(pl_sctp_packet.chunks[vl_k].sack,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].abort)){ // process ABORT |
| vl_handler_ret:=SCTP_message_incoming_abort_handler(pl_sctp_packet.chunks[vl_k].abort,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown)){ // process SHUTDOWN |
| vl_handler_ret:=SCTP_message_incoming_shutdown_handler(pl_sctp_packet.chunks[vl_k].shutdown,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown_ack)){ // process SHUTDOWN ACK |
| vl_handler_ret:=SCTP_message_incoming_shutdown_ack_handler(pl_sctp_packet.chunks[vl_k].shutdown_ack,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].shutdown_complete)){ // process SHUTDOWN COMPLETE |
| vl_handler_ret:=SCTP_message_incoming_shutdown_complete_handler(pl_sctp_packet.chunks[vl_k].shutdown_complete,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].heartbeat)){ // process HEARTBEAT |
| vl_handler_ret:=SCTP_message_incoming_heartbeat_handler(pl_sctp_packet.chunks[vl_k].heartbeat,pl_assoc_id,vl_response) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].heartbeat_ack)){ // process HEARTBEAT |
| vl_handler_ret:=SCTP_message_incoming_heartbeat_ack_handler(pl_sctp_packet.chunks[vl_k].heartbeat_ack,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].forward_tsn)){ // process FORWARD TSN |
| vl_handler_ret:=SCTP_message_incoming_forward_tsn_handler(pl_sctp_packet.chunks[vl_k].forward_tsn,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].iforward_tsn)){ // process FORWARD TSN |
| vl_handler_ret:=SCTP_message_incoming_iforward_tsn_handler(pl_sctp_packet.chunks[vl_k].iforward_tsn,pl_assoc_id) |
| } else if(ischosen(pl_sctp_packet.chunks[vl_k].re_config)){ // process FORWARD TSN |
| vl_handler_ret:=SCTP_message_incoming_re_config_handler(pl_sctp_packet.chunks[vl_k].re_config,vl_response,pl_assoc_id) |
| } |
| |
| |
| if(vl_handler_ret == -1 ){ |
| return // -1: Stop the further processing |
| } else if(vl_handler_ret == 1 ){ |
| vl_process_data:=true // 1: Continue with the next chunk, postprocess the data queues |
| } |
| } // for |
| |
| if(vl_process_data){ |
| // construct SACK |
| SCTP_data_gen_sack(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue, // The queue |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn, // The first expected tsn of the queue, if empty |
| vl_response.chunks[lengthof(vl_response.chunks)], |
| pl_assoc_id) // The chunk |
| |
| // if "deferred reset processing" is ongoing and received the missing data chunks |
| if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset) |
| and (SCTP_compare_tsn(SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset.last_assigned_tsn),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0) ){ |
| // then execute the reset |
| var integer vl_idx:=lengthof(vl_response.chunks) |
| vl_response.chunks[vl_idx]:={ |
| re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={} |
| |
| } |
| } |
| SCTP_reconf_process_ssn_reset(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset,vl_response.chunks[vl_idx].re_config,pl_assoc_id) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset:=omit |
| } |
| |
| // process the chunk queue and reconstruct the messages |
| SCTP_data_chunk2msg(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues,pl_assoc_id) |
| |
| // deliver the data to the user |
| SCTP_data_deliver(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues,pl_assoc_id) |
| |
| // Release the ack-ed and processed message from the rx chunk queue |
| SCTP_data_clean_tx_chunk_queue(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue,pl_assoc_id) |
| |
| |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.state == SCTP_ASSOC_SHUTDOWN_SENT){ |
| // We should send shutdown also |
| var integer vl_sack_idx:=lengthof(vl_response.chunks)-1 // the index of the sack chunk |
| var SCTP_Shutdown_chunk vl_sh:={ |
| chunk_type:=7, |
| flags:='00000000'B, |
| chunk_length:=0, |
| cum_tsn_ack:=vl_response.chunks[vl_sack_idx].sack.cum_tsn_ack |
| } |
| if(ispresent(vl_response.chunks[vl_sack_idx].sack.gap_blocks) or ispresent(vl_response.chunks[vl_sack_idx].sack.dup_tsns)){ |
| // SACK is needed, add the shutdown after the sack |
| vl_response.chunks[lengthof(vl_response.chunks)].shutdown:=vl_sh |
| } else { |
| // SACK is not needed, replace it |
| vl_response.chunks[vl_sack_idx].shutdown:=vl_sh |
| } |
| |
| // Restart the T2 timer |
| SCTP_timer_T2_restart(pl_assoc_id) |
| |
| } |
| } |
| |
| // Send the answer if need |
| if(lengthof(vl_response.chunks)>0){ |
| vl_response.common_header:={ |
| source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port, |
| verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag |
| } |
| |
| SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_response); |
| } |
| } |
| |
| // The return values of the chunk handlers: |
| // -1: Stop the further processing |
| // 0: Continue with the next chunk |
| // 1: Continue with the next chunk, postprocess the data queues |
| |
| |
| // Handles the reconfig chunk |
| function SCTP_message_incoming_re_config_handler(in SCTP_Re_Config_chunk pl_re_cfg, // The chunk to process |
| inout SCTP_Packet pl_response, |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| // validate the chunk |
| var boolean vl_sendresp:=false |
| var boolean vl_storeas_req:=false |
| |
| var SCTP_Re_Config_chunk vl_resp:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={} |
| } |
| |
| if( v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported // re config support |
| and ispresent(pl_re_cfg.params) |
| and ( |
| (lengthof(pl_re_cfg.params)==1) // one or two param present |
| or (lengthof(pl_re_cfg.params)==2) ) |
| ){ |
| |
| // process the requests |
| for(var integer vl_i:=0;vl_i<lengthof(pl_re_cfg.params);vl_i:=vl_i+1){ |
| if(ischosen(pl_re_cfg.params[vl_i].out_ssn_reset_req)){ |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id!=-1){ // timer of outgoing request is running |
| if(pl_re_cfg.params[vl_i].out_ssn_reset_req.reconf_resp_seq == v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq){ |
| // This is the acknowledgement of the sent incoming ssn reset request |
| // stop the timer |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_event_id:=-1 |
| |
| // No need to notify the user here, because the notification will be sent below |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false // no outgoing reconfig request as it is acked |
| v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=omit // acked |
| v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=-1 |
| } |
| |
| } |
| |
| // Check the seq no |
| var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].out_ssn_reset_req.reconf_req_seq |
| if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no |
| or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no |
| // this is just a hack to handle the case with two requests |
| and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp) |
| ){ |
| // retransmission |
| // send the latest response again |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp} |
| return 1 |
| } |
| |
| if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){ |
| // not the expected seq no |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=5, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| }} |
| } |
| } |
| return 0 |
| } |
| |
| // adjust the remote seq no |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no) |
| |
| if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset)){ |
| // reset already running |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=4, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| }} |
| } |
| } |
| return 0 |
| |
| } |
| |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_reset_allowed){ // Is SSN reset allowed? |
| if(SCTP_compare_tsn(SCTP_next_tsn(pl_re_cfg.params[vl_i].out_ssn_reset_req.last_assigned_tsn),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)>0){ |
| // The remote last assigned TSN is greater than the sack-ed |
| // Enter "deferred reset processing" |
| |
| // store the packet for the later processing |
| v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset:=pl_re_cfg.params[vl_i].out_ssn_reset_req |
| |
| // prepare response |
| vl_resp.params[lengthof(vl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=6, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| vl_sendresp:=true |
| |
| } else { |
| SCTP_reconf_process_ssn_reset(pl_re_cfg.params[vl_i].out_ssn_reset_req,vl_resp,pl_assoc_id) |
| vl_sendresp:=true |
| } |
| } else { |
| // send back the denied |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=2, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| }} |
| } |
| } |
| return 0 |
| |
| } |
| |
| } else if(ischosen(pl_re_cfg.params[vl_i].in_ssn_reset_req)){ |
| // check seq no |
| // Check the seq no |
| var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].in_ssn_reset_req.reconf_req_seq |
| if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no |
| or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no |
| // this is just a hack to handle the case with two requests |
| and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp) |
| ){ |
| // retransmission |
| // send the latest response again |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp} |
| return 1 |
| } |
| |
| if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){ |
| // not the expected seq no |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=5, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| }} |
| } |
| } |
| return 0 |
| } |
| |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing){ |
| // request is sent but not completed |
| // just ignore it |
| // The remote side will retransmit later |
| |
| } else { |
| // TODO: handle request overlap |
| // generate outgoing ssn reset request |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no) |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_reset_allowed){ // Is SSN reset allowed? |
| if(ispresent(pl_re_cfg.params[vl_i].in_ssn_reset_req.stream_numbers)){ |
| SCTP_reconf_generate_out_ssn_reset_req(pl_re_cfg.params[vl_i].in_ssn_reset_req.stream_numbers,vl_in_seq_no,vl_resp,pl_assoc_id) |
| } else { |
| SCTP_reconf_generate_out_ssn_reset_req({},vl_in_seq_no,vl_resp,pl_assoc_id) |
| } |
| |
| vl_sendresp:=true; |
| }else { |
| // send back the denied |
| vl_resp.params[lengthof(vl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=2, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| vl_sendresp:=true; |
| } |
| |
| } |
| } else if(ischosen(pl_re_cfg.params[vl_i].add_out_stream)){ |
| var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].add_out_stream.reconf_req_seq |
| if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no |
| or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no |
| // this is just a hack to handle the case with two requests |
| and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp) |
| ){ |
| // retransmission |
| // send the latest response again |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp} |
| return 1 |
| } |
| |
| if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){ |
| // not the expected seq no |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=5, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| }} |
| } |
| } |
| return 0 |
| } |
| |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no) |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_add_allowed){ // allowed? |
| // send back ok |
| vl_resp.params[lengthof(vl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=1, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| vl_sendresp:=true; |
| |
| // notify the user |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_OK, |
| method:={add_stream:={ |
| incoming:=false, |
| new_streams:=pl_re_cfg.params[vl_i].add_out_stream.stream_num |
| } |
| } |
| } |
| }, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| } else { |
| // send back the denied |
| vl_resp.params[lengthof(vl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=2, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| vl_sendresp:=true; |
| } |
| |
| } else if(ischosen(pl_re_cfg.params[vl_i].add_in_stream)){ |
| var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].add_in_stream.reconf_req_seq |
| if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no |
| or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no |
| // this is just a hack to handle the case with two requests |
| and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp) |
| ){ |
| // retransmission |
| // send the latest response again |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp} |
| return 1 |
| } |
| |
| if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){ |
| // not the expected seq no |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=5, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| }} |
| } |
| } |
| return 0 |
| } |
| |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no) |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.stream_add_allowed){ // Is allowed? |
| // send back ok |
| vl_resp.params[lengthof(vl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=1, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| vl_sendresp:=true; |
| |
| // send the add outgoing stream req |
| var SCTP_Packet vl_out_req:=c_empty_sctp_packet |
| vl_out_req.common_header:={ |
| source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port, |
| verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag |
| } |
| vl_out_req.chunks[0]:={ re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{ |
| add_out_stream:={ |
| param_type:=17, |
| param_length:=0, |
| reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num, |
| stream_num:=pl_re_cfg.params[vl_i].add_in_stream.stream_num, |
| reserved:=0 |
| } |
| }} |
| } |
| |
| } |
| v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num:=SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=vl_out_req.chunks[0].re_config |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0 |
| |
| // schedule the timer |
| SCTP_timer_reconf_start(pl_assoc_id) |
| |
| SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_out_req); |
| |
| } else { |
| // send back the denied |
| vl_resp.params[lengthof(vl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=2, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| vl_sendresp:=true; |
| } |
| |
| } else if(ischosen(pl_re_cfg.params[vl_i].ssn_tsn_reset_req)){ |
| var integer vl_in_seq_no:=pl_re_cfg.params[vl_i].ssn_tsn_reset_req.reconf_req_seq |
| if( ((SCTP_next_tsn(vl_in_seq_no)==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num) // prev received seq_no |
| or (SCTP_next_tsn(SCTP_next_tsn(vl_in_seq_no))==v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num)) // prev prev seg no |
| // this is just a hack to handle the case with two requests |
| and ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp) |
| ){ |
| // retransmission |
| // send the latest response again |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:=v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp} |
| return 1 |
| } |
| |
| if(vl_in_seq_no!=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num){ |
| // not the expected seq no |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=5, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| }} |
| } |
| } |
| return 0 |
| } |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_remote_seq_num:=SCTP_next_tsn(vl_in_seq_no) |
| |
| // process the request |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.assoc_reset_allowed){ // Is reset allowed? |
| // Compute an appropriate value for the Receiver's Next TSN |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:= (v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn + 2147483648) mod 4294967296 |
| |
| // Compute an appropriate value for the local endpoint's next TSN |
| // v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn already contains that value |
| |
| // Reset the stream and chunk queues |
| |
| // clear the incoming message queues |
| for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue |
| } |
| |
| // Clear incoming chunk queue |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue := c_empty_chunk_queue_db |
| |
| // Clear outgoing chunk queue |
| v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue := c_empty_chunk_queue_db |
| |
| // clear the outgoing message queues |
| for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues);vl_k:=vl_k+1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_k]:=c_empty_msg_queue |
| } |
| |
| // stop the T3 timer. |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){ |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1 |
| } |
| |
| vl_resp.params[lengthof(vl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=1, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| vl_sendresp:=true; |
| |
| // notify the user |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_OK, |
| method:={assoc_reset:={} |
| } |
| } |
| }, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| |
| } else { |
| // send back the denied |
| vl_resp.params[lengthof(vl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=vl_in_seq_no, |
| result:=2, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| vl_sendresp:=true; |
| } |
| |
| |
| } else if(ischosen(pl_re_cfg.params[vl_i].reconf_resp)){ |
| if(pl_re_cfg.params[vl_i].reconf_resp.reconf_resp_seq==v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq){ |
| // we got a response for our request |
| // stop the timers |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false |
| SCTP_timer_reconf_stop(pl_assoc_id) |
| |
| // get the request type |
| // check the answere code |
| if(pl_re_cfg.params[vl_i].reconf_resp.result==2){ |
| // denied |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_DENY, |
| method:=omit |
| } |
| }, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| SCTP_reconf_clear_marks(pl_assoc_id) |
| } else if(pl_re_cfg.params[vl_i].reconf_resp.result==6) { |
| // In progress, restart the timer and clear the retransmission counter |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0 |
| SCTP_timer_reconf_start(pl_assoc_id) |
| } else if(pl_re_cfg.params[vl_i].reconf_resp.result>2 and pl_re_cfg.params[vl_i].reconf_resp.result<6) { |
| // fail |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_FAIL, |
| method:=omit |
| } |
| }, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| SCTP_reconf_clear_marks(pl_assoc_id) |
| } else { |
| // success |
| // check the type of the request |
| var SCTP_parameters vl_req |
| if(SCTP_reconf_get_out_req( v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req,vl_req)){ |
| if(ischosen(vl_req.add_out_stream)){ |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_OK, |
| method:= {add_stream:={ |
| incoming:= true, |
| new_streams:=vl_req.add_out_stream.stream_num |
| } |
| } |
| } |
| }, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| } else if(ischosen(vl_req.out_ssn_reset_req)){ |
| // reset the ssn's |
| // the queues are already marked |
| for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){ |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k].flag){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue |
| } |
| } |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_OK, |
| method:= {stream_reset:={ |
| incoming:= true, |
| streams:=vl_req.out_ssn_reset_req.stream_numbers |
| } |
| } |
| } |
| }, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| } else if(ischosen(vl_req.ssn_tsn_reset_req)){ |
| // reset the assoc |
| // clear the incoming message queues |
| for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_k:=vl_k+1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_k]:=c_empty_msg_queue |
| } |
| |
| // Clear incoming chunk queue |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue := c_empty_chunk_queue_db |
| |
| // Clear outgoing chunk queue |
| v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue := c_empty_chunk_queue_db |
| |
| // clear the outgoing message queues |
| for(var integer vl_k:=0;vl_k<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues);vl_k:=vl_k+1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.tx_msg_queues[vl_k]:=c_empty_msg_queue |
| } |
| |
| // stop the T3 timer. |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){ |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1 |
| } |
| |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:= pl_re_cfg.params[vl_i].reconf_resp.sender_next_tsn |
| |
| v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn:=pl_re_cfg.params[vl_i].reconf_resp.receiver_next_tsn |
| |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_OK, |
| method:= {assoc_reset:={ |
| } |
| } |
| }}, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| } |
| } |
| } |
| |
| |
| } else if(pl_re_cfg.params[vl_i].reconf_resp.reconf_resp_seq==v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq){ |
| // we got a response for our request of do something with the incoming streams |
| // Just stop the timer as we should receive matching request |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=false |
| SCTP_timer_reconf_stop(pl_assoc_id) |
| |
| // check the answere code |
| if(pl_re_cfg.params[vl_i].reconf_resp.result==2){ |
| // denied |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_DENY, |
| method:=omit |
| } |
| }, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| } else if(pl_re_cfg.params[vl_i].reconf_resp.result>2) { |
| // fail |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_FAIL, |
| method:=omit |
| } |
| }, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| } |
| } |
| // else do nothing |
| } |
| |
| |
| |
| } // for |
| |
| |
| } else { |
| // protocol error |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ |
| error_:={ |
| chunk_type := 9, |
| flags:= '00000000'B, |
| chunk_length:=0, |
| params:={ |
| {protocol_violation:={cause_code:=13,cause_length:=0,reason:=''O}} |
| } |
| } |
| } |
| } |
| |
| if(vl_sendresp){ |
| pl_response.chunks[lengthof(pl_response.chunks)]:={re_config:=vl_resp} |
| v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp:=vl_resp |
| |
| } |
| |
| return 0 |
| } |
| |
| function SCTP_reconf_get_out_req(in SCTP_Re_Config_chunk pl_chunk, out SCTP_parameters pl_req) return boolean { |
| |
| if(ispresent(pl_chunk.params)){ |
| for(var integer vl_i; vl_i<lengthof(pl_chunk.params);vl_i:=vl_i+1){ |
| if(ischosen(pl_chunk.params[vl_i].out_ssn_reset_req) |
| or ischosen(pl_chunk.params[vl_i].add_out_stream) |
| or ischosen(pl_chunk.params[vl_i].ssn_tsn_reset_req)){ |
| pl_req:=pl_chunk.params[vl_i] |
| return true |
| } |
| } |
| } |
| return false |
| } |
| function SCTP_reconf_clear_marks(in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT{ |
| |
| for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i].flag:=false; |
| } |
| } |
| function SCTP_reconf_generate_out_ssn_reset_req( in integer_list pl_stream_list, |
| in integer pl_in_seq_no, |
| inout SCTP_Re_Config_chunk pl_resp, |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT{ |
| |
| var integer vl_idx:=lengthof(pl_resp.params) |
| pl_resp.params[vl_idx]:={out_ssn_reset_req:={ |
| param_type:=13, |
| param_length:=0, |
| reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num, |
| reconf_resp_seq:=pl_in_seq_no, |
| last_assigned_tsn:=SCTP_prev_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn), |
| stream_numbers:=omit |
| }} |
| |
| if(lengthof(pl_stream_list)>0){ |
| pl_resp.params[vl_idx].out_ssn_reset_req.stream_numbers:=pl_stream_list |
| // mark the lists as closed |
| for(var integer vl_i:=0;vl_i<lengthof(pl_stream_list);vl_i:=vl_i+1){ |
| var integer vl_stream_idx |
| if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id, |
| pl_stream_list[vl_i], |
| vl_stream_idx |
| )!=-1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx].flag:=true; |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx+1].flag:=true; |
| } |
| |
| } |
| |
| } else { |
| // mark all stream as closed |
| for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i].flag:=true; |
| } |
| |
| } |
| |
| v_assoc_db.associations[pl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num:=SCTP_next_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_own_seq_num) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_req:=pl_resp |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_ongoing:=true |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconfig_retransmit_counter:=0 |
| |
| // schedule the timer |
| SCTP_timer_reconf_start(pl_assoc_id) |
| |
| } |
| |
| function SCTP_reconf_process_ssn_reset(in SCTP_Out_SSN_Reset_req_parameter pl_re_cfg, // The chunk to process |
| inout SCTP_Re_Config_chunk pl_resp, |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT{ |
| var SCTP_Notification_data vl_note_data:={ |
| assoc_id:=pl_assoc_id, |
| notification := { |
| reconfig:={ |
| result:=RECONFIG_OK, |
| method:={stream_reset:={ |
| incoming:=false, |
| streams:={} |
| } |
| } |
| } |
| }, |
| options := omit |
| } |
| |
| if(ispresent(pl_re_cfg.stream_numbers)){ |
| // reset only the specified streams |
| for(var integer vl_i:=0;vl_i<lengthof(pl_re_cfg.stream_numbers);vl_i:=vl_i+1){ |
| var integer vl_stream_idx |
| vl_note_data.notification.reconfig.method.stream_reset.streams[lengthof(vl_note_data.notification.reconfig.method.stream_reset.streams)]:=pl_re_cfg.stream_numbers[vl_i] |
| if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id, |
| pl_re_cfg.stream_numbers[vl_i], |
| vl_stream_idx |
| )!=-1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx]:=c_empty_msg_queue |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_stream_idx+1]:=c_empty_msg_queue |
| } |
| } |
| } else { |
| // reset all streams |
| for(var integer vl_i:=0;vl_i<lengthof(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues);vl_i:=vl_i+1){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues[vl_i]:=c_empty_msg_queue |
| } |
| } |
| |
| // generate the ack message |
| pl_resp.params[lengthof(pl_resp.params)]:={ |
| reconf_resp:={ |
| param_type:=16, |
| param_length:=0, |
| reconf_resp_seq:=pl_re_cfg.reconf_req_seq, |
| result:=1, |
| sender_next_tsn:=omit, |
| receiver_next_tsn:=omit |
| } |
| } |
| v_assoc_db.associations[pl_assoc_id].assoc_data.last_reconf_resp:=pl_resp |
| // Notify the user |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_note_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| } |
| |
| // process I-forward TSN chunk |
| function SCTP_message_incoming_iforward_tsn_handler(in SCTP_IForward_TSN_chunk pl_fwd_tsn, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| |
| if(SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<0){ |
| // The new TSN has been acked already. Just trigger the SACK |
| return 1 |
| } |
| |
| // Clean up the data queue |
| // Mark every hole as processed packet until the new tsn |
| // The data processor part will do the magic later |
| |
| var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.first_element |
| |
| while(vl_idx!=-1){ |
| if( SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].tsn)<0){ |
| // the tsn of the chunk is greater than the new tsn |
| break; // we are done |
| } |
| |
| if(not ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].chunk)){ |
| // A hole, mark it. |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].mark_flag:= true |
| } |
| vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].next_element |
| } |
| |
| // clean up the queues |
| for(var integer vl_i:=0;vl_i<lengthof(pl_fwd_tsn.stream_ssn_list);vl_i:=vl_i+1){ |
| SCTP_message_queue_skip_ssn(pl_assoc_id,pl_fwd_tsn.stream_ssn_list[vl_i].stream_id,pl_fwd_tsn.stream_ssn_list[vl_i].mid mod 65536,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues ) |
| } |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(pl_fwd_tsn.new_tsn) |
| return 1 // trigger the data processing |
| } |
| |
| |
| // process forward TSN chunk |
| function SCTP_message_incoming_forward_tsn_handler(in SCTP_Forward_TSN_chunk pl_fwd_tsn, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| |
| if(SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<0){ |
| // The new TSN has been acked already. Just trigger the SACK |
| return 1 |
| } |
| |
| // Clean up the data queue |
| // Mark every hole as processed packet until the new tsn |
| // The data processor part will do the magic later |
| |
| var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.first_element |
| |
| while(vl_idx!=-1){ |
| if( SCTP_compare_tsn(pl_fwd_tsn.new_tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].tsn)<0){ |
| // the tsn of the chunk is greater than the new tsn |
| break; // we are done |
| } |
| |
| if(not ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].chunk)){ |
| // A hole, mark it. |
| v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].mark_flag:= true |
| } |
| vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue.chunk_queue[vl_idx].next_element |
| } |
| |
| // clean up the queues |
| for(var integer vl_i:=0;vl_i<lengthof(pl_fwd_tsn.stream_ssn_list);vl_i:=vl_i+1){ |
| SCTP_message_queue_skip_ssn(pl_assoc_id,pl_fwd_tsn.stream_ssn_list[vl_i].stream_id,pl_fwd_tsn.stream_ssn_list[vl_i].ssn,v_assoc_db.associations[pl_assoc_id].assoc_data.rx_msg_queues ) |
| } |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(pl_fwd_tsn.new_tsn) |
| return 1 // trigger the data processing |
| } |
| |
| // Mark the messages as skiped based in the forward tsn chunk data |
| function SCTP_message_queue_skip_ssn( in integer pl_assoc_id, // The association |
| in integer pl_stream_id, |
| in integer pl_skipped_ssn, |
| inout SCTP_msg_queue_list_t pl_msg_queue_list |
| ) runs on SCTP_Engine_CT { |
| |
| var integer vl_stream_idx:=-1; // index of the stream in the pl_msg_queue_list |
| if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id, |
| pl_stream_id, |
| vl_stream_idx |
| )==-1){ |
| // not seen stream |
| // create it |
| vl_stream_idx:=lengthof(pl_msg_queue_list) |
| // ordered queue |
| pl_msg_queue_list[vl_stream_idx]:=c_empty_msg_queue; |
| pl_msg_queue_list[vl_stream_idx].next_ssn:=SCTP_next_ssn(pl_skipped_ssn); // store the next expected ssn |
| |
| // undered queue |
| pl_msg_queue_list[vl_stream_idx+1]:=c_empty_msg_queue; |
| pl_msg_queue_list[vl_stream_idx+1].next_ssn:=-1; // Not used queue |
| |
| } else { |
| var integer vl_idx:=pl_msg_queue_list[vl_stream_idx].messages.first_element |
| while(vl_idx!=-1){ |
| if(SCTP_compare_ssn(SCTP_data_get_chunk_ssn(pl_msg_queue_list[vl_stream_idx].messages.chunk_queue[vl_idx].chunk),pl_skipped_ssn )<=0){ |
| // ssn less than the last skiped, mark it |
| pl_msg_queue_list[vl_stream_idx].messages.chunk_queue[vl_idx].mark_flag:=true |
| } else { |
| break; //no more ssn to mark |
| } |
| } |
| |
| if(SCTP_compare_ssn(pl_msg_queue_list[vl_stream_idx].next_ssn,pl_skipped_ssn)<=0){ |
| // afdjust the next ssn if needed |
| pl_msg_queue_list[vl_stream_idx].next_ssn:=SCTP_next_ssn(pl_skipped_ssn) |
| } |
| |
| } |
| |
| } |
| |
| // Handles the incoming HEARTBEAT_ACK |
| function SCTP_message_incoming_heartbeat_ack_handler(in SCTP_Heartbeat_ack_chunk pl_hb_ack, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| |
| // reset the hearbeat counter |
| v_assoc_db.associations[pl_assoc_id].assoc_data.hb_retrial_counter:=0 |
| return 0 |
| } |
| // Handles the incoming HEARTBEAT |
| function SCTP_message_incoming_heartbeat_handler(in SCTP_Heartbeat_chunk pl_hb, // The chunk to process |
| in integer pl_assoc_id, // The association |
| inout SCTP_Packet pl_response // Add the heartbeat_ack to the response |
| ) runs on SCTP_Engine_CT return integer { |
| |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case ( (SCTP_ASSOC_COOKIE_ECHOED,SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_RECEIVED)){ |
| // A receiver of a HEARTBEAT MUST respond to a |
| // HEARTBEAT with a HEARTBEAT-ACK after entering the COOKIE-ECHOED state |
| // (INIT sender) or the ESTABLISHED state (INIT receiver), up until |
| // reaching the SHUTDOWN-SENT state (SHUTDOWN sender) or the SHUTDOWN- |
| // ACK-SENT state (SHUTDOWN receiver). |
| |
| // Add the heartbeat ack chunk to the response |
| pl_response.chunks[lengthof(pl_response.chunks)]:={heartbeat_ack:={ chunk_type :=5, |
| flags := '00000000'B, |
| chunk_length := 0, |
| params :=pl_hb.params |
| }} |
| |
| } |
| } |
| |
| |
| |
| return 0 |
| } |
| |
| // Handles the incoming SHUTDOWN COMPLETE |
| function SCTP_message_incoming_shutdown_complete_handler(in SCTP_ShutdownComplete_chunk pl_shutdown, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case ( (SCTP_ASSOC_SHUTDOWN_SENT,SCTP_ASSOC_SHUTDOWN_ACK_SENT)){ |
| |
| // Notify the user |
| var SCTP_Shutdown_data vl_sh_data:={ |
| assoc_id:=pl_assoc_id, |
| graceful_shutdown := true, |
| reason := "Shutdown finished" |
| } |
| |
| p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| SCTP_assoc_set_closed(pl_assoc_id) |
| |
| } |
| } |
| return -1 |
| |
| } |
| |
| // Handles the incoming SHUTDOWN ACK |
| function SCTP_message_incoming_shutdown_ack_handler(in SCTP_Shutdown_ack_chunk pl_shutdown, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case ( (SCTP_ASSOC_SHUTDOWN_SENT,SCTP_ASSOC_SHUTDOWN_ACK_SENT)){ |
| |
| |
| SCTP_timer_stop_all(pl_assoc_id) |
| // Send Shutdown Complete |
| SCTP_message_send_shutdown_complete(-,-,pl_assoc_id) |
| |
| // delay the shutdown ind in order to give a chance to deliver the shutdown complete message |
| // The user may destroy the underlying transport in the case of shutdown ind |
| v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_CLOSE_TIME_WAIT |
| |
| v_assoc_db.associations[pl_assoc_id].assoc_data.close_timer:=SCTP_event_schedule(pl_assoc_id,c_timer_close,v_assoc_db.associations[pl_assoc_id].assoc_data.rto) |
| |
| |
| } |
| } |
| return -1 |
| } |
| |
| |
| // Handles the incoming SHUTDOWN |
| function SCTP_message_incoming_shutdown_handler(in SCTP_Shutdown_chunk pl_shutdown, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case ( (SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING)){ |
| // Set the state |
| v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_SHUTDOWN_RECEIVED |
| |
| // Process the cum_tsn_ack |
| SCTP_message_cumulative_ack_handler(pl_shutdown.cum_tsn_ack,pl_assoc_id) |
| // Schedule the sending of the data |
| // The data send scheduler will check the state and if no outstanding data |
| // will send shutdown ack and move to |
| SCTP_data_send_scheduler(pl_assoc_id) |
| |
| // Notify the user |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.state==SCTP_ASSOC_ESTABLISHED){ |
| var SCTP_Notification_data vl_notification:={ |
| assoc_id := pl_assoc_id, |
| notification:= {comm_lost := {}}, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| } |
| |
| } |
| case (SCTP_ASSOC_SHUTDOWN_RECEIVED){ |
| // Process the cum_tsn_ack |
| SCTP_message_cumulative_ack_handler(pl_shutdown.cum_tsn_ack,pl_assoc_id) |
| // Schedule the sending of the data |
| // The data send scheduler will check the state and if no outstanding data |
| // will send shutdown ack and move to |
| SCTP_data_send_scheduler(pl_assoc_id) |
| |
| } |
| case (SCTP_ASSOC_SHUTDOWN_SENT){ |
| // Set the state |
| v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_SHUTDOWN_RECEIVED |
| // The data send scheduler will check the state and if no outstanding data |
| // will send shutdown ack |
| SCTP_data_send_scheduler(pl_assoc_id) |
| } |
| } |
| return 0 |
| } |
| |
| // Handles the incoming ABORT |
| function SCTP_message_incoming_abort_handler(in SCTP_abort_chunk pl_abort, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| |
| // Notify the user |
| var SCTP_Notification_data vl_notification:={ |
| assoc_id := pl_assoc_id, |
| notification:= {comm_lost := {}}, |
| options := omit |
| } |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| var SCTP_Shutdown_data vl_sh_data:={ |
| assoc_id:=pl_assoc_id, |
| graceful_shutdown := false, |
| reason := "Abort received" |
| } |
| |
| // Store the reason if available |
| if(ispresent(pl_abort.params)){ |
| vl_sh_data.reason:=log2str(pl_abort.params) |
| } |
| |
| p_sctp_ind_api.call(S_SCTP_Shutdown_ind:{vl_sh_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| SCTP_assoc_set_closed(pl_assoc_id) |
| // Stop the further processing |
| return -1 |
| } |
| |
| // Handles the incoming SACK |
| function SCTP_message_incoming_sack_handler(in SCTP_SAck_chunk pl_sack, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case ((SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_RECEIVED)){ |
| var integer vl_tsn_comp:=SCTP_message_cumulative_ack_handler( pl_sack.cum_tsn_ack,pl_assoc_id) |
| if(vl_tsn_comp==-1){ |
| // TSN already acked |
| // do nothing |
| return 0 // next chunk |
| } |
| // update the peers window size |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_a_rwnd:= pl_sack.a_rwnd |
| if(vl_tsn_comp==0){ |
| // No new TSN are ACK-ed. |
| if(( pl_sack.a_rwnd==0) and // the peers window is closed |
| (v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter == v_assoc_db.associations[pl_assoc_id].assoc_data.proto_params.assoc_max_retrans) // retransmitting the zero probe, last trial |
| ){ |
| // Don't let the retransmitt counter overflow |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter := v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter - 1 |
| } |
| } else { |
| // stop the T3 timer, the SCTP_data_send_scheduler will restart it if needed |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event!=-1){ |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1 |
| } |
| // Something ack-ed -> try to send the remaining data |
| SCTP_data_send_scheduler(pl_assoc_id) |
| |
| } |
| |
| // TODO: handle gaps for fast retransmit |
| |
| } |
| } |
| return 0 |
| |
| } |
| |
| // Processes the cumulative TSN ack |
| // return value: |
| // -1: The acked TSN is less than the last acked |
| // 0: The acked TSN is the same as the last acked |
| // 1: New TSN is acked |
| function SCTP_message_cumulative_ack_handler(in integer pl_ack, in integer pl_assoc_id) runs on SCTP_Engine_CT return integer { |
| var integer vl_tsn_comp:=SCTP_compare_tsn( pl_ack,v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn) |
| if(vl_tsn_comp==-1){ |
| // TSN already acked |
| // do nothing |
| return -1 //The acked TSN is less than the last acked |
| } |
| if(vl_tsn_comp==0){ |
| // No new TSN are ACK-ed. |
| return 0 |
| } |
| // New TSN's acked |
| // The remote side is alive, clear the retransmit counter |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter := 0 |
| |
| // remove the acked TSN-s from the tx chunk queue. |
| var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element |
| var integer acked_bytes:=0 |
| while((vl_idx!=-1) and (SCTP_compare_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].tsn,pl_ack)<1) ){ |
| acked_bytes:=acked_bytes+SCTP_data_get_chunk_size(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].chunk) |
| |
| // decrease the flight size |
| v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size:=v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size-SCTP_data_get_chunk_size(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.chunk_queue[vl_idx].chunk) |
| // remove the chunk |
| SCTP_Chunk_queue_pop(v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue) |
| // next chunk idx |
| vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.tx_chunk_queue.first_element |
| } |
| //adjust the cwnd |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd<v_assoc_db.associations[pl_assoc_id].assoc_data.ssthresh){ |
| // slow-start case |
| if((v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+acked_bytes) >= (v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd-c_min_chunk_size)){ |
| // The cwnd is fully utilized |
| v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:= v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd + SCTP_min(acked_bytes,v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu) |
| } |
| } else { |
| // congestion avoidance case |
| v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes := v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes + acked_bytes |
| if((v_assoc_db.associations[pl_assoc_id].assoc_data.flight_size+acked_bytes) >= (v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd-c_min_chunk_size)){ |
| // The cwnd is fully utilized |
| v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd:= v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd + v_assoc_db.associations[pl_assoc_id].assoc_data.pmtu |
| v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes := v_assoc_db.associations[pl_assoc_id].assoc_data.partially_acked_bytes - v_assoc_db.associations[pl_assoc_id].assoc_data.cwnd |
| } |
| |
| } |
| |
| // Remember to the last ack-ed |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn:= pl_ack |
| |
| |
| return 1 // 1: New TSN is acked |
| |
| } |
| |
| // Handles the incoming DATA |
| function SCTP_message_incoming_data_handler(in SCTP_Chunk pl_data, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| //log("data handler state, ", v_assoc_db.associations[pl_assoc_id].assoc_data.state) |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case (SCTP_ASSOC_ESTABLISHED,SCTP_ASSOC_SHUTDOWN_PENDING,SCTP_ASSOC_SHUTDOWN_SENT){ |
| if(SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_data),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)>=0){ |
| // put the chunk into the rx data chunk queue |
| SCTP_data_insert_into_rx(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_chunk_queue, // The queue |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn, // The first expected tsn of the queue, if empty |
| pl_data, |
| pl_assoc_id) // The chunk |
| |
| } //else { |
| // The TSN is smaller than the last ack-ed |
| // just drop the chunk |
| // do nothing |
| //} |
| //log("return 1") |
| return 1 |
| } |
| } |
| //log("return 0") |
| return 0 |
| } |
| // Handles the incoming COOKIE-ECHO |
| function SCTP_message_incoming_cookie_echo_handler(in SCTP_CookieEcho_chunk pl_cookie_echo, // The chunk to process |
| in integer pl_assoc_id, // The association |
| inout SCTP_Packet pl_response, // place the cookie echo into this message, if DATA chunk is also received, the SACK will be added later |
| in boolean pl_cookie_preprocessed |
| ) runs on SCTP_Engine_CT return integer { |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case (SCTP_ASSOC_ESTABLISHED){ |
| // Process the messages and send answers if needed |
| if(pl_cookie_preprocessed){ |
| // Only the cookie ack is should be sent |
| pl_response.chunks[lengthof(pl_response.chunks)]:={ |
| cookie_ack:={ |
| chunk_type := 11, |
| flags := '00000000'B, |
| chunk_length := 0 |
| } |
| } |
| } else { |
| // TODO: Restart |
| } |
| } |
| } |
| return 0 |
| } |
| // Handles the incoming COOKIE-ACK |
| function SCTP_message_incoming_cookie_ack_handler(in SCTP_Cookie_ack_chunk pl_cookie_ack, // The chunk to process |
| in integer pl_assoc_id // The association |
| ) runs on SCTP_Engine_CT return integer { |
| |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case (SCTP_ASSOC_COOKIE_ECHOED) { |
| v_assoc_db.associations[pl_assoc_id].assoc_data.state:=SCTP_ASSOC_ESTABLISHED |
| // Start hearbeat |
| SCTP_hearbeat_start(pl_assoc_id) |
| |
| |
| // Stop the T1_cookie timer |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1 |
| |
| // Notify the user |
| var SCTP_Notification_data vl_notification:={ |
| assoc_id := pl_assoc_id, |
| notification:= {comm_up := {}}, |
| options := { |
| {i_data_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported}, |
| {reconfig_params:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params}, |
| {forward_tsn_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported} |
| |
| } |
| } |
| SCTP_congestion_set_init_params(pl_assoc_id) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_last_sack_tsn := (v_assoc_db.associations[pl_assoc_id].assoc_data.own_tsn - 1) mod 4294967296 |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| // Reset the retransmit counter |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0 |
| } |
| } |
| return 0 |
| } |
| // Handles the incoming INIT-ACK chunk |
| function SCTP_message_incoming_init_ack_handler(in SCTP_InitAck_chunk pl_init_ack, // The chunk to process |
| in integer pl_assoc_id, // The association |
| in integer pl_chunk_idx // The index of the chunk in the incoming message |
| ) runs on SCTP_Engine_CT return integer { |
| select(v_assoc_db.associations[pl_assoc_id].assoc_data.state){ |
| case (SCTP_ASSOC_COOKIE_WAIT) { |
| |
| // Only the INIT ACK is acceptable |
| if( pl_chunk_idx == 0) { |
| |
| // Find the cookie for echo |
| var integer vl_cookie_idx:=-1 |
| var integer vl_supported_ext_idx:=-1 |
| var integer vl_supported_forward_tsn_idx:=-1 |
| |
| if(ispresent(pl_init_ack.params)){ |
| for(var integer vl_l:=0;vl_l<lengthof(pl_init_ack.params);vl_l:=vl_l+1){ |
| if(ischosen(pl_init_ack.params[vl_l].state_cookie)){ |
| vl_cookie_idx:=vl_l |
| } |
| if(ischosen(pl_init_ack.params[vl_l].supported_extensions)){ |
| vl_supported_ext_idx:=vl_l |
| } |
| if(ischosen(pl_init_ack.params[vl_l].forward_tsn_supported)){ |
| vl_supported_forward_tsn_idx:=vl_l |
| } |
| |
| } |
| } |
| |
| // Is there a cookie? |
| if(vl_cookie_idx==-1){ |
| // No? |
| // Drop the packet |
| log("No cookie in the INIT_ACK. Dropped. ") |
| return -1; |
| } |
| // The INIT_ack seems OK |
| |
| // Stop the timer |
| SCTP_event_remove(v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event) |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:=-1 |
| |
| // Update the database |
| v_assoc_db.associations[pl_assoc_id].assoc_data:={ |
| state:=SCTP_ASSOC_COOKIE_ECHOED, |
| remote_tag:=pl_init_ack.init_tag, |
| remote_tsn:=pl_init_ack.init_tsn, |
| remote_os:=pl_init_ack.os, |
| remote_mis:=pl_init_ack.mis, |
| remote_a_rwnd:=pl_init_ack.a_rwnd, |
| state_cookie:=pl_init_ack.params[vl_cookie_idx].state_cookie.state_cookie, |
| reconf_remote_seq_num:=pl_init_ack.init_tsn |
| } |
| |
| // Check the extensions support |
| var boolean vl_idata:=false |
| var boolean vl_reconf:=false |
| if(vl_supported_ext_idx!=-1){ |
| |
| for(var integer vl_i:=0;vl_i<lengthof(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions);vl_i:=vl_i+1){ |
| if(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions[vl_i]==64){ // IDATA chunk |
| vl_idata:=true |
| } |
| if(pl_init_ack.params[vl_supported_ext_idx].supported_extensions.supported_extensions[vl_i]==130){ // IDATA chunk |
| vl_reconf:=true |
| } |
| } |
| } |
| |
| v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported:=vl_idata and v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported |
| v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported:= vl_reconf and v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported |
| v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported:= (vl_supported_forward_tsn_idx!=-1) and v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported |
| |
| |
| // Send the Cookie Echo |
| SCTP_message_send_cookie_echo(pl_assoc_id) |
| |
| // Reset the retransmit counter and start the timer |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_counter:=0 |
| v_assoc_db.associations[pl_assoc_id].assoc_data.retransmit_timer_event:= |
| SCTP_event_schedule(pl_assoc_id,c_timer_T1_cookie,v_assoc_db.associations[pl_assoc_id].assoc_data.rto) |
| } else { |
| // Something else received |
| // Drop it |
| log("Unexpected packet, dorpped: ") |
| return -1 |
| } |
| } |
| } |
| |
| return 0 |
| } |
| |
| //Clean ups the acked and processed messages from the rx chunk queue |
| function SCTP_data_clean_tx_chunk_queue(inout SCTP_Chunk_queue_db_t pl_queue, in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| var integer vl_chunk_idx:=pl_queue.first_element |
| while(vl_chunk_idx!=-1){ |
| if( pl_queue.chunk_queue[vl_chunk_idx].mark_flag ){ |
| // If the first element is marked, it is acked |
| SCTP_Chunk_queue_pop(pl_queue) |
| |
| if(v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked==vl_chunk_idx){ |
| v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked:=-1 |
| break; // We reached the last acked chunk |
| } else { |
| vl_chunk_idx:=pl_queue.first_element |
| } |
| } else { |
| break; // The chunk can not be removed from the incoming queue |
| } |
| } |
| |
| } |
| |
| // Delivers the whole messages to the user |
| function SCTP_data_deliver(inout SCTP_msg_queue_list_t pl_msg_queue_list, in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| // Iterate through the rx message queues and deliver the messages |
| for(var integer vl_idx:=0; vl_idx<lengthof(pl_msg_queue_list);vl_idx:=vl_idx+1){ |
| // The ordered queue |
| var integer vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element |
| while(vl_msg_idx!=-1){ |
| // check the messages in the stream queue |
| if(SCTP_message_get_E_bit(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk)=='1'B){ |
| // Whole message |
| var SCTP_MSG_data vl_msg_data |
| SCTP_data_get_msg_data(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk,vl_msg_data,pl_assoc_id) // Copy the msg |
| // Deliver it |
| p_sctp_ind_api.call(S_SCTP_Received_ind:{vl_msg_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| // Remove from the queue |
| SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages) |
| |
| vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element |
| } else { |
| if(not pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag){ |
| break; // we reached the first not fully received message |
| } else { |
| // the msg are marked as skiped, skip it |
| SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages) |
| |
| vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element |
| } |
| } |
| } |
| |
| // The unordered queue |
| vl_idx:=vl_idx+1 |
| vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element |
| while(vl_msg_idx!=-1){ |
| // check the messages in the stream queue |
| if( (not pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag) // already processed |
| and (SCTP_message_get_E_bit(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk)=='1'B) // End flag |
| ){ |
| // Whole message |
| var SCTP_MSG_data vl_msg_data |
| SCTP_data_get_msg_data(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk,vl_msg_data,pl_assoc_id) // Copy the msg |
| // Deliver it |
| p_sctp_ind_api.call(S_SCTP_Received_ind:{vl_msg_data},nowait) to v_assoc_db.associations[pl_assoc_id].assoc_data.rem_comp |
| |
| // Mark the msg |
| pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag:=true |
| } |
| |
| // detect the skiped element |
| // For unordered message no direct signal of the skiped messages |
| // But if there is a next message and the tsn of the last fragement < acked tsn |
| // the message is skiped. |
| // Not allowed to interleave two unordered message of the same queue |
| if((pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].next_element!=-1) |
| and (SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].chunk),v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0)){ |
| // Mark the msg |
| pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag:=true |
| } |
| |
| if(pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].mark_flag // already processed |
| and (vl_msg_idx==pl_msg_queue_list[vl_idx].messages.first_element) // first of the queue |
| ){ |
| |
| // remove it from the queue |
| SCTP_Chunk_queue_pop(pl_msg_queue_list[vl_idx].messages) |
| vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.first_element |
| |
| } else { |
| vl_msg_idx:=pl_msg_queue_list[vl_idx].messages.chunk_queue[vl_msg_idx].next_element |
| } |
| |
| } |
| } |
| |
| } |
| |
| |
| // Insert the chunk into the rx queue |
| // pl_queue - the queue, where to insert the chunk |
| // pl_expected_tsn - If the queue is empty the new first chunk should use this tsn |
| // pl_chunk - the chunk to insert |
| function SCTP_data_insert_into_rx(inout SCTP_Chunk_queue_db_t pl_queue, in integer pl_expected_tsn,in SCTP_Chunk pl_chunk, in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked |
| var integer vl_tsn:=pl_expected_tsn |
| var integer vl_chunk_tsn:=SCTP_data_get_chunk_tsn(pl_chunk) |
| // set the index to pint to the first not acked item |
| if(vl_idx==-1){ |
| // the last_acked was removed from the queue |
| // take the first element |
| vl_idx:=pl_queue.first_element |
| } else { |
| // pick the next one |
| vl_tsn:=SCTP_next_tsn(pl_queue.chunk_queue[vl_idx].tsn) // set the tsn counter |
| vl_idx:=pl_queue.chunk_queue[vl_idx].next_element |
| } |
| // find the place of the item |
| |
| while(true){ |
| if(vl_idx==-1){ |
| // We need to add item |
| SCTP_Chunk_queue_push(pl_queue,c_empty_Chunk_queue_element) |
| vl_idx:=pl_queue.last_element |
| pl_queue.chunk_queue[vl_idx].tsn:=vl_tsn |
| } |
| if(pl_queue.chunk_queue[vl_idx].tsn == vl_chunk_tsn){ |
| break; // we found the place |
| } |
| // move to the next item |
| vl_idx:=pl_queue.chunk_queue[vl_idx].next_element |
| // take next tsn |
| vl_tsn:=SCTP_next_tsn(vl_tsn) |
| } |
| |
| // store the chunk |
| pl_queue.chunk_queue[vl_idx].chunk:=pl_chunk |
| |
| } |
| |
| // Construct the messages from the rx chunk queue |
| // scans the rx chunk queue for a messages |
| |
| function SCTP_data_chunk2msg(inout SCTP_Chunk_queue_db_t pl_queue, inout SCTP_msg_queue_list_t pl_msg_queue_list, in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| var integer vl_chunk_idx:=pl_queue.first_element |
| var boolean vl_gap_found:=false |
| |
| while(vl_chunk_idx!=-1){ |
| if(ispresent(v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset)){ |
| // See rfc6525 5.2.2. E2: |
| if(SCTP_compare_tsn(pl_queue.chunk_queue[vl_chunk_idx].tsn,v_assoc_db.associations[pl_assoc_id].assoc_data.deffered_reset.last_assigned_tsn )>0){ |
| break; |
| } |
| } |
| |
| |
| if(not pl_queue.chunk_queue[vl_chunk_idx].mark_flag){ |
| // The chunk is not marked. Means was not put into the rx stream message queue |
| if(ispresent(pl_queue.chunk_queue[vl_chunk_idx].chunk)){ |
| // Find the stream queue of the message |
| var integer vl_stream_idx:=-1; // index of the stream in the pl_msg_queue_list |
| var integer vl_stream_id:=SCTP_data_get_chunk_stream_id(pl_queue.chunk_queue[vl_chunk_idx].chunk) // The stream identifier |
| var integer vl_stream_seq_no |
| if(SCTP_int2int_get(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id, |
| vl_stream_id, |
| vl_stream_idx |
| )==-1){ |
| // New incoming stream |
| // Is this the first message in the stream? |
| if( ((not vl_gap_found) // All of the previous chunk received, this is the first chunk of the first message |
| // of the stream. Or the remote side is sending a shit (or I forgot something) |
| or ( (SCTP_message_get_U_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk))=='1'B) ) // or first chunk of an unordered msg |
| and (SCTP_message_get_B_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk)=='1'B) |
| ){ |
| vl_stream_idx:=lengthof(pl_msg_queue_list) |
| // ordered queue |
| pl_msg_queue_list[vl_stream_idx]:=c_empty_msg_queue; |
| pl_msg_queue_list[vl_stream_idx].next_ssn:=-1; // Not used queue |
| |
| // undered queue |
| pl_msg_queue_list[vl_stream_idx+1]:=c_empty_msg_queue; |
| pl_msg_queue_list[vl_stream_idx+1].next_ssn:=-1; // Not used queue |
| if(SCTP_int2int_add(v_assoc_db.associations[pl_assoc_id].assoc_data.rx_stream_idx_map_id, |
| vl_stream_id, |
| vl_stream_idx) == -1){ |
| log("Intenal error") |
| } |
| } else { |
| vl_stream_idx:=-1 // We can't be sure that this is the first message of the stream |
| } |
| } |
| |
| if(vl_stream_idx!=-1){ |
| // valid stream |
| if(SCTP_message_get_U_bit(pl_queue.chunk_queue[vl_chunk_idx].chunk)=='0'B){ |
| //ordered |
| if(pl_msg_queue_list[vl_stream_idx].next_ssn!=-1 or (not vl_gap_found)){ |
| // In the case of the ordered message |
| // Try to store the first message if all of the previous chunk has been received. |
| SCTP_data_find_store_chunk(pl_msg_queue_list[vl_stream_idx],pl_queue.chunk_queue[vl_chunk_idx],false) |
| } |
| } else { |
| //unordered |
| SCTP_data_find_store_chunk(pl_msg_queue_list[vl_stream_idx+1],pl_queue.chunk_queue[vl_chunk_idx],true) |
| } |
| } |
| |
| if(SCTP_compare_tsn(SCTP_data_get_chunk_tsn(pl_queue.chunk_queue[vl_chunk_idx].chunk), v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)<=0){ |
| // if the tsn of the packet is already ack-ed mark it for clean up |
| pl_queue.chunk_queue[vl_chunk_idx].mark_flag:=true |
| } |
| } else { |
| // found a gap |
| vl_gap_found:=true |
| } |
| } |
| vl_chunk_idx:=pl_queue.chunk_queue[vl_chunk_idx].next_element |
| } |
| } |
| |
| // Stores the message in the msg queue if applicable |
| function SCTP_data_find_store_chunk(inout SCTP_msg_queue_t pl_msg_db, inout SCTP_Chunk_queue_element_t pl_chunk, in boolean pl_unordered){ |
| if(((pl_msg_db.next_ssn==-1) or // not used queue |
| (pl_msg_db.next_ssn==SCTP_data_get_chunk_ssn(pl_chunk.chunk)) // next expected message |
| or pl_unordered) // Unordered |
| and |
| (SCTP_message_get_B_bit(pl_chunk.chunk)=='1'B) // First chunk of the message |
| ){ |
| // unused queue, first chunk of the message |
| // or the first chunk of the next expected message |
| // Add the chunk as the first chunk of the next message |
| SCTP_Chunk_queue_push(pl_msg_db.messages,pl_chunk) |
| pl_chunk.mark_flag:=true // Chunk is in the rx stream message queue |
| pl_msg_db.next_ssn:=SCTP_next_ssn(SCTP_data_get_chunk_ssn(pl_chunk.chunk)) // Store the next ssn of the stream |
| pl_msg_db.messages.chunk_queue[pl_msg_db.messages.last_element].tsn:=SCTP_next_tsn(SCTP_data_get_chunk_reassembly_seq_no(pl_chunk.chunk)) // store the next TSN/FSN |
| return |
| } |
| |
| // Find the message |
| var integer vl_idx:=pl_msg_db.messages.first_element |
| while(vl_idx!=-1){ |
| if(SCTP_data_get_chunk_ssn(pl_msg_db.messages.chunk_queue[vl_idx].chunk) == SCTP_data_get_chunk_ssn(pl_chunk.chunk)){ |
| // This is the message we're looking for |
| // Is this the next chunk? |
| if(pl_msg_db.messages.chunk_queue[vl_idx].tsn== SCTP_data_get_chunk_reassembly_seq_no(pl_chunk.chunk)){ |
| // yes, this is |
| // append the data part. |
| SCTP_data_chunk_append_data(pl_msg_db.messages.chunk_queue[vl_idx].chunk,pl_chunk.chunk) |
| |
| // updated the stored tsn of the message for support the RFC3758 |
| SCTP_data_chunk_copy_tsn(pl_msg_db.messages.chunk_queue[vl_idx].chunk,pl_chunk.chunk) |
| |
| pl_chunk.mark_flag:=true // Chunk is in the rx stream message queue |
| |
| pl_msg_db.messages.chunk_queue[vl_idx].tsn := SCTP_next_tsn(pl_msg_db.messages.chunk_queue[vl_idx].tsn) // store th enext expected TSN/FSN |
| // Set E bit if needed |
| if(SCTP_message_get_E_bit(pl_chunk.chunk)=='1'B){ |
| SCTP_message_set_E_bit('1'B,pl_msg_db.messages.chunk_queue[vl_idx].chunk) |
| } |
| |
| |
| } |
| return |
| } |
| |
| vl_idx:=pl_msg_db.messages.chunk_queue[vl_idx].next_element |
| |
| } |
| |
| |
| } |
| |
| // Generates SACK from the rx queue |
| // pl_queue - the queue, where to insert the chunk |
| // pl_remote_tsn - The last acked tsn+1 |
| // pl_chunk - the chunk to return the SACK |
| function SCTP_data_gen_sack(in SCTP_Chunk_queue_db_t pl_queue, in integer pl_remote_tsn,out SCTP_Chunk pl_chunk, in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| var integer vl_idx:=v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked |
| // set the index to point to the first not acked item |
| //log("*************************") |
| //log(pl_queue) |
| //log(pl_remote_tsn) |
| //log(pl_assoc_id) |
| //log("*************************") |
| if(vl_idx==-1){ |
| // the last_acked was removed from the queue |
| // take the first element |
| vl_idx:=pl_queue.first_element |
| if(vl_idx!=-1 and |
| ((not ispresent(pl_queue.chunk_queue[vl_idx].chunk)) or pl_queue.chunk_queue[vl_idx].mark_flag ) |
| ){ |
| // The first element represents a non marked hole |
| vl_idx:=-1 // no new chunk to ack |
| } |
| } |
| // find the last element before a hole/end |
| while( (vl_idx != -1) and // valid element |
| (pl_queue.chunk_queue[vl_idx].next_element!=-1) and // has a next element |
| (ispresent(pl_queue.chunk_queue[pl_queue.chunk_queue[vl_idx].next_element].chunk) or pl_queue.chunk_queue[pl_queue.chunk_queue[vl_idx].next_element].mark_flag)// wich is not a hole |
| ){ |
| vl_idx:=pl_queue.chunk_queue[vl_idx].next_element // advance to it |
| } |
| |
| var integer vl_sack_tsn |
| if(vl_idx != -1){ |
| vl_sack_tsn:=pl_queue.chunk_queue[vl_idx].tsn |
| } else { |
| vl_sack_tsn:= SCTP_prev_tsn(pl_remote_tsn) |
| } |
| |
| pl_chunk.sack:={ |
| chunk_type:= 3, |
| flags := '00000000'B, |
| chunk_length:=0, |
| cum_tsn_ack:=vl_sack_tsn, |
| a_rwnd:=65535, |
| no_of_gap_blocks:=0, |
| no_of_dup_tsn:=0, |
| gap_blocks:=omit, |
| dup_tsns:=omit |
| } |
| |
| // set the pointers |
| v_assoc_db.associations[pl_assoc_id].assoc_data.idx_of_last_acked:=vl_idx |
| v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn:=SCTP_next_tsn(vl_sack_tsn) |
| } |
| |
| //Process the incoming INIT & COOKIE ECHO |
| function SCTP_message_incoming_init_cookie_handler(in integer pl_assoc_id, |
| in integer pl_transport_id, |
| in SCTP_Packet pl_sctp_packet) runs on SCTP_Engine_CT { |
| |
| if(ischosen(pl_sctp_packet.chunks[0].init)){ |
| // INIT chunk received. Generate INIT-ACK |
| var SCTP_Packet vl_response := c_empty_sctp_packet |
| var SCTP_InitAck_chunk vl_initack_chunk := c_empty_init_ack_chunk |
| var integer vl_init_tag:=SCTP_rnd_32bit_int() |
| |
| //Check the supported extensions |
| var boolean vl_idata:=false |
| var boolean vl_reconf:=false |
| var boolean vl_fwd_tsn:=false |
| if(ispresent(pl_sctp_packet.chunks[0].init.params)){ |
| for(var integer vl_i:=0;vl_i<lengthof(pl_sctp_packet.chunks[0].init.params);vl_i:=vl_i+1){ |
| if(ischosen(pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions)){ |
| for(var integer vl_k:=0;vl_k<lengthof(pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions);vl_k:=vl_k+1){ |
| if((pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions[vl_k]==64)){ |
| vl_idata:=true |
| } |
| if((pl_sctp_packet.chunks[0].init.params[vl_i].supported_extensions.supported_extensions[vl_k]==130)){ |
| vl_reconf:=true |
| } |
| } |
| |
| } else if(ischosen(pl_sctp_packet.chunks[0].init.params[vl_i].forward_tsn_supported)){ |
| vl_fwd_tsn:=true |
| } |
| } |
| } |
| vl_idata:=vl_idata and v_assoc_db.associations[pl_assoc_id].assoc_data.i_data_supported // support if requested both by the user and remote side |
| |
| vl_reconf:=vl_reconf and v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params.is_supported // support if requested both by the user and remote side |
| |
| vl_fwd_tsn :=vl_fwd_tsn and v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported |
| |
| var octetstring pl_cookie:=SCTP_gen_state_cookie(v_assoc_db.associations[pl_assoc_id].assoc_data.cookie_lifespan, |
| vl_init_tag,// pl_own_init_tag, |
| pl_sctp_packet.chunks[0].init.init_tag, // pl_remote_init_tag, |
| 0,// pl_tie_tag, 0 means no tie |
| pl_sctp_packet.chunks[0].init.init_tsn, // pl_remote_tsn, |
| pl_sctp_packet.chunks[0].init.os, // pl_remote_os, |
| pl_sctp_packet.chunks[0].init.mis ,// pl_remote_mis, |
| pl_sctp_packet.chunks[0].init.a_rwnd, // pl_remote_a_rwnd, |
| vl_idata, |
| vl_reconf, |
| vl_fwd_tsn , |
| v_secret_key // pl_key |
| ) |
| vl_initack_chunk:={ |
| init_tag := vl_init_tag, |
| a_rwnd := 65535, |
| os := 65535, |
| mis := 65535, |
| init_tsn := vl_init_tag, |
| params :={ |
| {state_cookie:={param_type:=7,param_length:=0,state_cookie:=pl_cookie}} |
| } |
| } |
| if(vl_idata or vl_reconf){ |
| var integer vl_idx:=0 |
| if(ispresent(vl_initack_chunk.params)){ |
| vl_idx:=lengthof(vl_initack_chunk.params) |
| } |
| vl_initack_chunk.params[vl_idx].supported_extensions:={ |
| param_type := 32776, |
| param_length :=0, |
| supported_extensions:={} |
| } |
| if(vl_idata){ |
| vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=64 |
| } |
| if(vl_reconf){ |
| vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions[lengthof(vl_initack_chunk.params[vl_idx].supported_extensions.supported_extensions)]:=130 |
| } |
| } |
| if(vl_fwd_tsn){ |
| var integer vl_idx:=0 |
| if(ispresent(vl_initack_chunk.params)){ |
| vl_idx:=lengthof(vl_initack_chunk.params) |
| } |
| vl_initack_chunk.params[vl_idx].forward_tsn_supported:={ |
| param_type := 49152, |
| param_length :=0 |
| } |
| } |
| vl_response:={ |
| common_header := { |
| source_port:=pl_sctp_packet.common_header.destination_port, |
| destination_port:=pl_sctp_packet.common_header.source_port, |
| verification_tag:=pl_sctp_packet.chunks[0].init.init_tag |
| }, |
| chunks := { {init_ack:=vl_initack_chunk} } |
| } |
| SCTP_send_packet(pl_transport_id,vl_response); |
| |
| } else { |
| // Cookie Echo received, process it. |
| // Allocate new assoc |
| var integer vl_new_assoc:=SCTP_assoc_construct_from_cookie_echo(pl_sctp_packet,pl_transport_id,pl_assoc_id); |
| |
| if(vl_new_assoc < 0){ // The cookie check failed |
| // drop the packet |
| return; |
| } |
| |
| // Check the cookie lifetime |
| if(v_assoc_db.associations[vl_new_assoc].assoc_data.cookie_validity==1){ // Cookie expired |
| // Send abort with erro chunk |
| |
| // Construct the error chunk |
| var SCTP_error_chunk vl_error_chunk:= { |
| chunk_type := 9, |
| flags := '00000000'B, |
| chunk_length := 0, |
| params := { |
| {stale_cookie:={cause_code:=3, cause_length:=0,measure:=0}} |
| } |
| } |
| |
| // send the abort |
| SCTP_message_send_abort(pl_transport_id,pl_sctp_packet,vl_new_assoc,-,{{error_:=vl_error_chunk}}) |
| |
| // drop the association |
| SCTP_free_assoc_entry(vl_new_assoc) |
| return; // We're done |
| } |
| // Add the connection to the id map |
| if(SCTP_add_id_map(vl_new_assoc,pl_transport_id,v_assoc_db.associations[vl_new_assoc].assoc_data.local_port,v_assoc_db.associations[vl_new_assoc].assoc_data.remote_port)==-1){ |
| // Stop further processing |
| return; |
| } |
| |
| // Send connected |
| var SCTP_Connected_data vl_connected:={ |
| assoc_id := vl_new_assoc, |
| local_sctp_port := v_assoc_db.associations[vl_new_assoc].assoc_data.local_port, |
| remote_sctp_port := v_assoc_db.associations[vl_new_assoc].assoc_data.remote_port, |
| listen_assoc_id := pl_assoc_id |
| } |
| |
| p_sctp_ind_api.call(S_SCTP_Connected_ind:{vl_connected},nowait) to v_assoc_db.associations[vl_new_assoc].assoc_data.rem_comp |
| |
| // Send notification to the owner of the listen port |
| var SCTP_Notification_data vl_notification:={ |
| assoc_id := vl_new_assoc, |
| notification:= {comm_up := {}}, |
| options := { |
| {i_data_supported:=v_assoc_db.associations[vl_new_assoc].assoc_data.i_data_supported}, |
| {reconfig_params:=v_assoc_db.associations[pl_assoc_id].assoc_data.reconf_params}, |
| {forward_tsn_supported:=v_assoc_db.associations[pl_assoc_id].assoc_data.forward_tsn_supported} |
| |
| } |
| } |
| |
| |
| |
| p_sctp_ind_api.call(S_SCTP_Notification_ind:{vl_notification},nowait) to v_assoc_db.associations[vl_new_assoc].assoc_data.rem_comp |
| v_assoc_db.associations[vl_new_assoc].assoc_data.state:=SCTP_ASSOC_ESTABLISHED; |
| SCTP_hearbeat_start(vl_new_assoc) |
| |
| // Reset the retransmit counter |
| v_assoc_db.associations[vl_new_assoc].assoc_data.retransmit_counter:=0 |
| |
| SCTP_congestion_set_init_params(vl_new_assoc) |
| // Set the last acked TSN to the expeted -1 |
| v_assoc_db.associations[vl_new_assoc].assoc_data.remote_last_sack_tsn := (v_assoc_db.associations[vl_new_assoc].assoc_data.own_tsn - 1) mod 4294967296 |
| |
| // Send the packet for further processing |
| SCTP_message_incoming_handler(vl_new_assoc,pl_sctp_packet,true) |
| |
| |
| |
| } |
| } |
| |
| // Process the Out of the blue packets |
| // See 8.4 of rfc |
| function SCTP_message_process_oob_packet(in integer pl_transport_id, // Where to send the abort |
| in SCTP_Packet pl_sctp_packet // The incoming packet |
| ) runs on SCTP_Engine_CT { |
| // first search map the chunks |
| // Interested ones: |
| // - Abort |
| var boolean vl_abort_found:=false |
| // - Init |
| var boolean vl_init_found:=false |
| // - Cookie Echo |
| var boolean vl_cookie_echo_found:=false |
| // - Shutdown Complete |
| var boolean vl_shutdown_complete_found:=false |
| // - Shutdown Ack |
| var boolean vl_shutdown_ack_found:=false |
| // - CookieAck |
| var boolean vl_cookie_ack_found:=false |
| |
| var integer vl_i:=0 |
| for(vl_i:=0;vl_i<lengthof(pl_sctp_packet.chunks);vl_i:=vl_i+1) { |
| if(ischosen(pl_sctp_packet.chunks[vl_i].abort)){ |
| vl_abort_found:=true |
| continue |
| } else if (ischosen(pl_sctp_packet.chunks[vl_i].init)) { |
| vl_init_found:=true |
| continue |
| } else if (ischosen(pl_sctp_packet.chunks[vl_i].cookie_echo)) { |
| vl_cookie_echo_found:=true |
| continue |
| } else if (ischosen(pl_sctp_packet.chunks[vl_i].shutdown_complete)) { |
| vl_shutdown_complete_found:=true |
| continue |
| } else if (ischosen(pl_sctp_packet.chunks[vl_i].shutdown_ack)) { |
| vl_shutdown_ack_found:=true |
| continue |
| } else if (ischosen(pl_sctp_packet.chunks[vl_i].cookie_ack)) { |
| vl_cookie_ack_found:=true |
| continue |
| } |
| } |
| |
| // The packet should be dropped if |
| // - Abort |
| // - CookieAck |
| // - Shutdown Complete |
| if( vl_abort_found or vl_cookie_ack_found or vl_shutdown_complete_found){ |
| return; // do nothing |
| } else if(vl_init_found){ |
| // Init -> send abort - tag handled automatically |
| SCTP_message_send_abort(pl_transport_id,pl_sctp_packet) |
| } else if(vl_cookie_echo_found) { |
| // Cookie Echo -> Send abort, but check the cookie first |
| // construct a temp assoc entry, it checks the validity of the cookie also |
| var integer pl_temp_id:=SCTP_assoc_construct_from_cookie_echo(pl_sctp_packet,pl_transport_id); |
| |
| if(pl_temp_id>=0){ |
| // send abort |
| SCTP_message_send_abort(pl_transport_id,-,pl_temp_id); |
| // free the assoc entry |
| SCTP_free_assoc_entry(pl_temp_id); |
| } |
| // else just drop the message |
| } else if(vl_shutdown_ack_found) { |
| // SHUTDOWN ACK -> send SHUTDOWN COMPLETE |
| SCTP_message_send_shutdown_complete(pl_transport_id,pl_sctp_packet) |
| } else { |
| // any other case: send abort |
| SCTP_message_send_abort(pl_transport_id,pl_sctp_packet) |
| } |
| |
| } |
| |
| |
| |
| // Send the shutdown ack message |
| function SCTP_message_send_shutdown_ack(in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| |
| var SCTP_Packet vl_sctp := c_empty_sctp_packet |
| vl_sctp:={ |
| common_header:={ |
| source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port, |
| verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag |
| }, |
| chunks:={{shutdown_ack:={chunk_type:=8,flags:='00000000'B,chunk_length:=0}}} |
| } |
| |
| |
| // Send it |
| SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp); |
| |
| |
| } |
| |
| // Send the shutdown message |
| function SCTP_message_send_shutdown(in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| |
| var SCTP_Packet vl_sctp := c_empty_sctp_packet |
| vl_sctp:={ |
| common_header:={ |
| source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port, |
| verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag |
| }, |
| chunks:={{shutdown:={chunk_type:=7,flags:='00000000'B,chunk_length:=0,cum_tsn_ack:=SCTP_prev_tsn(v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tsn)}}} |
| } |
| |
| |
| // Send it |
| SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp); |
| |
| |
| } |
| |
| function SCTP_message_send_heartbeat(in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| var SCTP_Packet vl_sctp := c_empty_sctp_packet |
| vl_sctp:={ |
| common_header:={ |
| source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port, |
| verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag |
| }, |
| chunks:={{heartbeat:={chunk_type:=4, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={{heartbeat_info:={ param_type:=1, |
| param_length := 0, |
| info:=SCTP_misc_float2oct(t_run_time.read) |
| }}} |
| }}} |
| } |
| |
| |
| // Send it |
| SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp); |
| |
| } |
| |
| // Send abort message |
| // The abort message can be constructed from |
| // -- assoc data |
| // -- incoming sctp packet |
| function SCTP_message_send_abort(in integer pl_transport_id:=-1, // Where to send the abort |
| in SCTP_Packet pl_sctp_packet := c_empty_sctp_packet, // The incoming packet caused the abort |
| in integer pl_assoc_id:=-1, // The assoc id if applicable |
| in SCTP_Error_cause_list pl_cause_list:= {}, // the cause the abort |
| in SCTP_Chunk_List pl_chunk_list :={} // The chunks include before the abort chunk |
| ) runs on SCTP_Engine_CT { |
| |
| var SCTP_Packet vl_response := c_empty_sctp_packet |
| var SCTP_abort_chunk vl_abort_chunk := c_empty_abort_chunk |
| |
| if(pl_assoc_id != -1){ |
| // We have association |
| // Use the data from the database |
| pl_transport_id := v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id |
| vl_response.common_header:={ |
| source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port, |
| verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag |
| } |
| vl_abort_chunk.T_flag:='0'B // The verification_tag is not reflected |
| } else { |
| // Out of the blue packet |
| if( (lengthof(pl_sctp_packet.chunks) > 0) |
| and ischosen(pl_sctp_packet.chunks[0].init) |
| ){ |
| // INIT chunk, get the Initiate Tag for the verification_tag |
| vl_response.common_header:={ |
| source_port:=pl_sctp_packet.common_header.destination_port, |
| destination_port:=pl_sctp_packet.common_header.source_port, |
| verification_tag:=pl_sctp_packet.chunks[0].init.init_tag |
| } |
| vl_abort_chunk.T_flag:='0'B // The verification_tag is not reflected |
| } else { |
| // reflect the verification_tag |
| vl_response.common_header:={ |
| source_port:=pl_sctp_packet.common_header.destination_port, |
| destination_port:=pl_sctp_packet.common_header.source_port, |
| verification_tag:=pl_sctp_packet.common_header.verification_tag |
| } |
| vl_abort_chunk.T_flag:='1'B // The verification_tag is reflected |
| } |
| |
| } |
| |
| // Construct the message |
| |
| // Add cause list if needed |
| if(lengthof(pl_cause_list) > 0){ |
| vl_abort_chunk.params:=pl_cause_list |
| } |
| |
| // Add the other chunks before the abort chunk |
| if(lengthof(pl_chunk_list) > 0){ |
| vl_response.chunks:=pl_chunk_list |
| } |
| |
| // Add the abort chunk |
| vl_response.chunks[lengthof(vl_response.chunks)].abort:=vl_abort_chunk; |
| |
| // Send it |
| SCTP_send_packet(pl_transport_id,vl_response); |
| |
| } |
| |
| // Send abort message |
| // The shutdown complete message can be constructed from |
| // -- assoc data |
| // -- incoming sctp packet |
| function SCTP_message_send_shutdown_complete(in integer pl_transport_id:=-1, // Where to send |
| in SCTP_Packet pl_sctp_packet := c_empty_sctp_packet, // The incoming packet |
| in integer pl_assoc_id:=-1 // The assoc id if applicable |
| ) runs on SCTP_Engine_CT { |
| |
| var SCTP_Packet vl_response := c_empty_sctp_packet |
| var SCTP_ShutdownComplete_chunk vl_sh_comp_chunk := c_empty_shutdown_complete_chunk |
| |
| if(pl_assoc_id != -1){ |
| // We have association |
| // Use the data from the database |
| pl_transport_id := v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id |
| vl_response.common_header:={ |
| source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port, |
| verification_tag:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag |
| } |
| vl_sh_comp_chunk.T_flag:='0'B // The verification_tag is not reflected |
| } else { |
| // Out of the blue packet |
| // reflect the verification_tag |
| vl_response.common_header:={ |
| source_port:=pl_sctp_packet.common_header.destination_port, |
| destination_port:=pl_sctp_packet.common_header.source_port, |
| verification_tag:=pl_sctp_packet.common_header.verification_tag |
| } |
| vl_sh_comp_chunk.T_flag:='1'B // The verification_tag is reflected |
| } |
| |
| |
| // Construct the message |
| |
| // Add the chunk |
| vl_response.chunks[lengthof(vl_response.chunks)].shutdown_complete:=vl_sh_comp_chunk; |
| |
| // Send it |
| SCTP_send_packet(pl_transport_id,vl_response); |
| |
| } |
| |
| // Send the sctp packet |
| function SCTP_send_packet(in integer pl_transport_id, // Where to send the packet |
| in SCTP_Packet pl_sctp_packet) runs on SCTP_Engine_CT { |
| |
| var SCTP_Engine_packet vl_packet:={ |
| transport_id:=pl_transport_id, |
| sctp_packet:=f_SCTP_enc(pl_sctp_packet) |
| } |
| |
| p_packet_port.send(vl_packet) |
| } |
| |
| // Send message with Cookie Echo |
| function SCTP_message_send_cookie_echo(in integer pl_assoc_id) runs on SCTP_Engine_CT { |
| |
| var SCTP_Packet vl_sctp := c_empty_sctp_packet |
| vl_sctp:={ |
| common_header :={ |
| source_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[pl_assoc_id].assoc_data.remote_port, |
| verification_tag := v_assoc_db.associations[pl_assoc_id].assoc_data.remote_tag |
| }, |
| chunks:={{cookie_echo:={chunk_type:=10, flags:= '00000000'B, chunk_length:=0,cookie:=v_assoc_db.associations[pl_assoc_id].assoc_data.state_cookie}}} |
| } |
| |
| // Send the packet |
| SCTP_send_packet(v_assoc_db.associations[pl_assoc_id].assoc_data.transport_id,vl_sctp) |
| } |
| /*****************************************************************************/ |
| // |
| // API functions |
| // |
| /*****************************************************************************/ |
| |
| // Handles the reconfig request |
| function SCTP_api_handle_reconfig(in SCTP_Reconfiguration_req vl_reconfig, // the listen parameters |
| in SCTP_Engine_CT vl_sender // The sender of the request |
| ) runs on SCTP_Engine_CT { |
| |
| var integer vl_assoc_id:=vl_reconfig.assoc_id |
| // Check the associ id validity |
| |
| if( (vl_assoc_id<0) or (vl_assoc_id>lengthof(v_assoc_db.associations)) // check boundary |
| or (not ispresent(v_assoc_db.associations[vl_assoc_id].assoc_data)) // Is used? |
| or (v_assoc_db.associations[vl_assoc_id].assoc_data.rem_comp != vl_sender) // Is my assoc? |
| ){ |
| // invalid assoc_id |
| p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_ID,"The assoc_id is invalid or belongs to another component"}) to vl_sender |
| return |
| } |
| |
| // check the state of the association |
| if(v_assoc_db.associations[vl_assoc_id].assoc_data.state!=SCTP_ASSOC_ESTABLISHED){ |
| // The sendrequest is allowed only in established state |
| p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_WRONG_ASSOC_STATE,"The association is not in the established state"}) to vl_sender |
| return |
| |
| } |
| |
| // Check if request is ongoing |
| if(v_assoc_db.associations[vl_assoc_id].assoc_data.reconfig_ongoing){ |
| p_sctp_req_api.raise(S_SCTP_reconfig, SCTP_operation_execption:{SCTP_ENG_RECONFIG_ONGOING,"There is an ongoing reconfig request. Please try again later"}) to vl_sender |
| return |
| |
| } |
| |
| var SCTP_Packet vl_sctp := c_empty_sctp_packet |
| vl_sctp:={ |
| common_header :={ |
| source_port:=v_assoc_db.associations[vl_assoc_id].assoc_data.local_port, |
| destination_port:=v_assoc_db.associations[vl_assoc_id].assoc_data.remote_port, |
| verification_tag := v_assoc_db.associations[vl_assoc_id].assoc_data.remote_tag |
| }, |
| chunks:={{ |
| re_config:={ |
| chunk_type:=130, |
| flags:='00000000'B, |
| chunk_length:=0, |
| params:={} |
| |
| } |
| }} |
| } |
| |
| // create and send the request |
| if(ischosen(vl_reconfig.reconf_method.assoc_reset)){ |
| vl_sctp.chunks[0].re_config.params[0]:={ |
| ssn_tsn_reset_req:={ |
| param_type:=15, |
| param_length:=0, |
| reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num |
| } |
| } |
| |
| v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num |
| |
| } else if(ischosen(vl_reconfig.reconf_method.stream_reset)){ |
| if(vl_reconfig.reconf_method.stream_reset.incoming){ |
| vl_sctp.chunks[0].re_config.params[0]:={ |
| in_ssn_reset_req:={ |
| param_type:=14, |
| param_length:=0, |
| reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num, |
| stream_numbers:=vl_reconfig.reconf_method.stream_reset.streams |
| } |
| } |
| v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num |
| |
| } else { |
| if(ispresent(vl_reconfig.reconf_method.stream_reset.streams)){ |
| SCTP_reconf_generate_out_ssn_reset_req( vl_reconfig.reconf_method.stream_reset.streams, |
| SCTP_prev_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.own_tsn), |
| vl_sctp.chunks[0].re_config, |
| vl_assoc_id |
| ) |
| } else { |
| SCTP_reconf_generate_out_ssn_reset_req( {}, |
| SCTP_prev_tsn(v_assoc_db.associations[vl_assoc_id].assoc_data.own_tsn), |
| vl_sctp.chunks[0].re_config, |
| vl_assoc_id |
| ) |
| } |
| v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_outgoing_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num |
| } |
| |
| } else { |
| // add stream req |
| if(vl_reconfig.reconf_method.add_stream.incoming){ |
| vl_sctp.chunks[0].re_config.params[0]:={ |
| add_in_stream:={ |
| param_type:=18, |
| param_length:=0, |
| reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num, |
| stream_num:=vl_reconfig.reconf_method.add_stream.new_streams, |
| reserved:=0 |
| } |
| } |
| v_assoc_db.associations[vl_assoc_id].assoc_data.ongoing_incoming_reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num |
| } else { |
| vl_sctp.chunks[0].re_config.params[0]:={ |
| add_out_stream:={ |
| param_type:=17, |
| param_length:=0, |
| reconf_req_seq:=v_assoc_db.associations[vl_assoc_id].assoc_data.reconf_own_seq_num, |
| stream_num:=vl_reconfig.reconf_method.add_stream.new_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) |
| } |
| |
| |
| } |