| /////////////////////////////////////////////////////////////////////////////// |
| // 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_reconfi
|