/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000-2023 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: M3UA_Emulation.ttcn // Reference: M3UA Protocol Emulation // Rev: R2A // Prodnr: CNL 113 537 // Updated: 2009-01-06 // Contact: http://ttcn.ericsson.se module M3UA_Emulation { modulepar { boolean tsp_logVerbose := false; float tsp_Timer := 2.0; // General timer used in M3UA emulation. float tsp_ASPUP_Resend_Timer := 2.0; float tsp_ASPAC_Resend_Timer := 2.0; float tsp_Assoc_Restart_Timer := 60.0; float tsp_Heartbeat_Timer := 30.0; integer tsp_SCTP_PayloadProtocolID := 3; // 3 for M3UA. boolean tsp_Enable_M3UA_Heartbeat := false; // Send SCTP packets periodically. boolean tsp_SCTP_Server_Mode := false; boolean tsp_M3UA_Server_Mode := false; } import from General_Types all; import from M3UA_Types all; import from SCTPasp_Types all; import from SCTPasp_PortType all; import from MTP3asp_Types all; type record of ASP_MTP3_TRANSFERreq TRANSFERreq_Buffer; type record SCTP_Association_Address { integer local_sctp_port, charstring local_ip_addr, integer remote_sctp_port, charstring remote_ip_addr } // Definition of M3UA_Entity which contains M3UA entity data. type record M3UA_Entity { M3UA_CommStatus commStatus optional, integer sCTP_Assoc_ID optional, M3UA_Routing_Context rctx optional, SCTP_Association_Address assoc } // Type for status of SCTP communication for an M3UA entity. type enumerated M3UA_CommStatus { aSP_Down_initial_State (0), aSP_Down_sCTP_Initialize_Done (1), aSP_Down_sCTP_Associate_done (2), aSP_Down_commUP_Received (3), aSP_Down_ASPUP_Sent (4), aSP_Inactive (5), aSP_Inact_ASPAC_Sent (6), aSP_Active (7) // aSPAC_Ack_Received } // We need an internal port to communicate with the MTP3 side. // internal in name type port MTP3asp_SP_PT_Int message { in ASP_MTP3_TRANSFERreq; out ASP_MTP3_TRANSFERind; // out ASP_MTP3_PAUSE; // out ASP_MTP3_RESUME; // out ASP_MTP3_STATUS; } with { extension "internal" } // M3UA emulation component. type component M3UA_CT { var M3UA_Entity v_Entity; var TRANSFERreq_Buffer v_TRANSFERreq_Buffer := {}; var ASP_SCTP v_ASP_SCTP; var ASP_SCTP_SEND_FAILED v_ASP_SCTP_SEND_FAILED; var ASP_SCTP_RESULT v_ASP_SCTP_RESULT; var ASP_SCTP_Connected v_ASP_SCTP_Connected; var ASP_SCTP_ASSOC_CHANGE v_ASP_SCTP_ASSOC_CHANGE; var ASP_SCTP_SHUTDOWN_EVENT v_ASP_SCTP_SHUTDOWN_EVENT; var PDU_M3UA v_PDU_M3UA; // Component timers. timer T_Timer := tsp_Timer; timer T_ASPUP_resend := tsp_ASPUP_Resend_Timer; timer T_ASPAC_resend := tsp_ASPAC_Resend_Timer; timer T_Heartbeat := tsp_Heartbeat_Timer; timer T_Assoc_restart := tsp_Assoc_Restart_Timer; // Port declarations. port MTP3asp_SP_PT_Int MTP3_SP_PORT; // Port towards MTP3/M3UA. port SCTPasp_PT SCTP_PORT; // Port towards target through SCTP. } //******************************** // Start of SCTP related templates //******************************** template ASP_SCTP t_S_SCTP_Send (in template integer pl_associationID, in template integer pl_streamID, in template octetstring pl_userData, in template integer pl_protocolID) := { client_id := pl_associationID, sinfo_stream := pl_streamID, sinfo_ppid := pl_protocolID, data := pl_userData } template ASP_SCTP_SEND_FAILED t_ASP_SCTP_SEND_FAILED (in template integer pl_streamID) := { client_id := pl_streamID } template ASP_SCTP_Listen t_ASP_SCTP_Listen (template charstring pl_local_hostname, template integer pl_local_portnumber) := { local_hostname := pl_local_hostname, local_portnumber := pl_local_portnumber } template ASP_SCTP_Connected tr_ASP_SCTP_Connected (template integer pl_client_id, template charstring pl_local_hostname, template integer pl_local_portnumber, template charstring pl_peer_hostname, template integer pl_peer_portnumber) := { client_id := pl_client_id, local_hostname := pl_local_hostname, local_portnumber := pl_local_portnumber, peer_hostname := pl_peer_hostname, peer_portnumber := pl_peer_portnumber } template ASP_SCTP_ConnectFrom t_ASP_SCTP_ConnectFrom (template charstring pl_local_hostname, template integer pl_local_portnumber, template charstring pl_peer_hostname, template integer pl_peer_portnumber) := { local_hostname := pl_local_hostname, local_portnumber := pl_local_portnumber, peer_hostname := pl_peer_hostname, peer_portnumber := pl_peer_portnumber } template ASP_SCTP_RESULT t_ASP_SCTP_RESULT (template integer pl_client_id, template boolean pl_error_status, template charstring pl_error_message) := { client_id := pl_client_id, error_status := pl_error_status, error_message := pl_error_message } template ASP_SCTP_ASSOC_CHANGE tr_S_SCTP_CommunicationUp (in template integer pl_associationID) := { client_id := pl_associationID, sac_state := SCTP_COMM_UP } template ASP_SCTP_ASSOC_CHANGE tr_S_SCTP_CommunicationLost (in template integer pl_associationID) := { client_id := pl_associationID, sac_state := SCTP_COMM_LOST } template ASP_SCTP_ASSOC_CHANGE tr_S_SCTP_ShutdownComplete (in template integer pl_associationID) := { client_id := pl_associationID, sac_state := SCTP_SHUTDOWN_COMP } template ASP_SCTP_SHUTDOWN_EVENT tr_S_SCTP_ShutdownEvent (in template integer pl_associationID) := { client_id := pl_associationID } template ASP_SCTP_ASSOC_CHANGE tr_S_SCTP_Restart (in template integer pl_associationID) := { client_id := pl_associationID, sac_state := SCTP_RESTART } template ASP_SCTP_ASSOC_CHANGE tr_S_SCTP_CANT_STR_ASSOC (in template integer pl_associationID) := { client_id := pl_associationID, sac_state := SCTP_CANT_STR_ASSOC } template ASP_SCTP tr_S_SCTP_DataArrive (in template integer pl_associationID, in template integer pl_streamID, in template integer pl_protocolID, in template PDU_SCTP pl_data) := { client_id := pl_associationID, sinfo_stream := pl_streamID, sinfo_ppid := pl_protocolID, data := pl_data } template ASP_SCTP_Close t_ASP_SCTP_Close (in template integer pl_associationID) := { client_id := pl_associationID } //****************************** // End of SCTP related templates //****************************** //***************************** // Start of M3UA PDU templates. //***************************** template PDU_M3UA t_PDU_M3UA_ASPUP (in template M3UA_ASP_Identifier pl_aSP_Identifier, in template M3UA_Info_String pl_info_String) := { m3UA_ASPUP := { version := '01'O, reserved := '00'O, messageClassAndType := '0301'O, messageLength := 0, messageParameters := { aSP_Identifier := pl_aSP_Identifier, info_String := pl_info_String } } } template PDU_M3UA t_PDU_M3UA_ASPUP_Ack := { m3UA_ASPUP_Ack := { version := '01'O, reserved := '00'O, messageClassAndType := '0304'O, messageLength := 0, messageParameters := { info_String := omit } } } template PDU_M3UA t_PDU_M3UA_ASPDN := { m3UA_ASPDN := { version := '01'O, reserved := '00'O, messageClassAndType := '0302'O, messageLength := 0, messageParameters := { info_String := omit } } } template PDU_M3UA t_PDU_M3UA_ASPDN_Ack := { m3UA_ASPDN_Ack := { version := '01'O, reserved := '00'O, messageClassAndType := '0305'O, messageLength := 0, messageParameters := { info_String := omit } } } template PDU_M3UA t_PDU_M3UA_ASPAC (in template M3UA_Traffic_Mode_Type pl_traffic_Mode_Type, in template M3UA_Routing_Context pl_routing_Context, in template M3UA_Info_String pl_info_String) := { m3UA_ASPAC := { version := '01'O, reserved := '00'O, messageClassAndType := '0401'O, messageLength := 0, messageParameters := { traffic_Mode_Type := pl_traffic_Mode_Type, routing_Context := pl_routing_Context, info_String := pl_info_String } } } template PDU_M3UA t_PDU_M3UA_ASPAC_Ack (in template M3UA_Traffic_Mode_Type pl_traffic_mode_type, in template M3UA_Routing_Context pl_routing_Context) := { m3UA_ASPAC_Ack := { version := '01'O, reserved := '00'O, messageClassAndType := '0403'O, messageLength := 0, messageParameters := { traffic_Mode_Type := pl_traffic_mode_type, routing_Context := pl_routing_Context, info_String := omit } } } template PDU_M3UA t_PDU_M3UA_ASPIA (in template M3UA_Routing_Context pl_routing_Context, in template M3UA_Info_String pl_info_String) := { m3UA_ASPIA := { version := '01'O, reserved := '00'O, messageClassAndType := '0402'O, messageLength := 0, messageParameters := { routing_Context := pl_routing_Context, info_String := pl_info_String } } } template PDU_M3UA t_PDU_M3UA_ASPIA_Ack (in template M3UA_Routing_Context pl_routing_Context) := { m3UA_ASPIA_Ack := { version := '01'O, reserved := '00'O, messageClassAndType := '0404'O, messageLength := 0, messageParameters := { routing_Context := pl_routing_Context, info_String := omit } } } template PDU_M3UA t_PDU_M3UA_Heartbeat (in template M3UA_Heartbeat_Data pl_heartbeat_Data) := { m3UA_BEAT := { version := '01'O, reserved := '00'O, messageClassAndType := '0303'O, messageLength := 0, messageParameters := { heartbeat_Data := pl_heartbeat_Data } } } template PDU_M3UA t_PDU_M3UA_Beat_Ack (in template M3UA_Heartbeat_Data pl_heartbeat_Data) := { m3UA_BEAT_Ack := { version := '01'O, reserved := '00'O, messageClassAndType := '0306'O, messageLength := 0, messageParameters := { heartbeat_Data := pl_heartbeat_Data } } } template PDU_M3UA t_PDU_M3UA_NOTIFY (in template M3UA_Status pl_status, in template M3UA_ASP_Identifier pl_aSP_Identifier, in template M3UA_Routing_Context pl_routing_Context, in template M3UA_Info_String pl_info_String) := { m3UA_NOTIFY := { version := '01'O, reserved := '00'O, messageClassAndType := '0001'O, messageLength := 0, messageParameters := { status := pl_status, aSP_Identifier := pl_aSP_Identifier, routing_Context := pl_routing_Context, info_String := pl_info_String } } } template PDU_M3UA t_PDU_M3UA_DATA (in template M3UA_Network_Appearance pl_network_Appearance, in template M3UA_Routing_Context pl_routing_Context, in template M3UA_Protocol_Data pl_protocol_Data, in template M3UA_Correlation_ID pl_correlation_ID) := { m3UA_DATA := { version := '01'O, reserved := '00'O, messageClassAndType := '0101'O, messageLength := 0, messageParameters := { network_Appearance := pl_network_Appearance, routing_Context := pl_routing_Context, protocol_Data := pl_protocol_Data, correlation_ID := pl_correlation_ID } } } template PDU_M3UA t_PDU_M3UA_DAVA (in template M3UA_Network_Appearance pl_network_Appearance, in template M3UA_Routing_Context pl_routing_Context, in template M3UA_Affected_Point_Codes pl_affected_Point_Codes, in template M3UA_Info_String pl_info_String) := { m3UA_DAVA := { version := '01'O, reserved := '00'O, messageClassAndType := '0202'O, messageLength := 0, messageParameters := { network_Appearance := pl_network_Appearance, routing_Context := pl_routing_Context, affected_Point_Codes := pl_affected_Point_Codes, info_String := pl_info_String } } } //************************** // End of M3UA PDU templates //************************** //********************************** // Start of M3UA parameter templates //********************************** template M3UA_Protocol_Data t_M3UA_Protocol_Data (template OCT4 pl_oPC, template OCT4 pl_dPC, template OCT1 pl_sI, template OCT1 pl_nI, template OCT1 pl_mP, template OCT1 pl_sLS, template octetstring pl_userProtocolData) := { tag := '0210'O, lengthInd := 0, oPC := pl_oPC, dPC := pl_dPC, sI := pl_sI, nI := pl_nI, mP := pl_mP, sLS := pl_sLS, userProtocolData := pl_userProtocolData } //******************************** // End of M3UA parameter templates //******************************** //*********************************** // Dynamic part of the M3UA emulation //*********************************** private template (value) M3UA_Routing_Context t_RoutingContext(OCT4 rctx) := { tag := '0006'O, lengthInd := 8, routingContext := rctx } private template (value) M3UA_Status t_Status(OCT2 status_type, OCT2 status_info) := { tag := '000d'O, lengthInd := 8, statusType := status_type, statusInfo := status_info } function f_M3UA_Emulation(SCTP_Association_Address pl_Boot, template (omit) integer rctx := omit) runs on M3UA_CT { // Initialize parameters from the test case. v_Entity.assoc := pl_Boot; v_Entity.commStatus := aSP_Down_initial_State; if (istemplatekind(rctx, "omit")) { v_Entity.rctx := omit; } else { v_Entity.rctx := valueof(t_RoutingContext(int2oct(valueof(rctx), 4))); } // At this point, we assume that the ports are already connected and mapped // properly by the user. log("*************************************************"); log("M3UA emulation initiated, the test can be started"); log("*************************************************"); f_Initialize_SCTP(); // Start the main function in an infinte loop. f_M3UA_ScanEvents(); } // Initialize the SCTP layer with parameters read from the configuration file. // We have only a single association. function f_Initialize_SCTP() runs on M3UA_CT { v_Entity.commStatus := aSP_Down_sCTP_Initialize_Done; if (tsp_SCTP_Server_Mode) { // Send out a LISTEN message. The communication status doesn't change // here. SCTP_PORT.send (t_ASP_SCTP_Listen(v_Entity.assoc.local_ip_addr, v_Entity.assoc.local_sctp_port)); } else { // Send ConnectFrom sequentially, wait for RESULT messages. f_Associate(); T_Assoc_restart.start; } if (tsp_SCTP_PayloadProtocolID == 3) { if (not tsp_M3UA_Server_Mode) { T_ASPUP_resend.start; T_ASPAC_resend.start; } if (tsp_Enable_M3UA_Heartbeat) { T_Heartbeat.start; } } } // Associate SCTP connection for a M3UA entity. function f_Associate() runs on M3UA_CT { SCTP_PORT.send(t_ASP_SCTP_ConnectFrom (v_Entity.assoc.local_ip_addr, v_Entity.assoc.local_sctp_port, v_Entity.assoc.remote_ip_addr, v_Entity.assoc.remote_sctp_port)); T_Timer.start; alt { [] SCTP_PORT.receive(t_ASP_SCTP_RESULT(*, ?, *)) -> value v_ASP_SCTP_RESULT { if (v_ASP_SCTP_RESULT.error_status) { log("Connect failed: ", v_ASP_SCTP_RESULT.error_message); } else { v_Entity.sCTP_Assoc_ID := v_ASP_SCTP_RESULT.client_id; v_Entity.commStatus := aSP_Down_sCTP_Associate_done; log("SCTP_ConnectResult -> connection established from: ", v_Entity.assoc.local_ip_addr, ":", v_Entity.assoc.local_sctp_port, " to server: ", v_Entity.assoc.remote_ip_addr, ":", v_Entity.assoc.remote_sctp_port, " association #", v_Entity.sCTP_Assoc_ID); if (tsp_logVerbose) { log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to ", v_Entity.commStatus); } } T_Timer.stop; } [] T_Timer.timeout { log("----------------------------------------------"); log("No response received to t_ASP_SCTP_ConnectFrom"); log("----------------------------------------------"); setverdict(fail); // mtc.stop; } } } // Starts M3UA emulation execution. function f_M3UA_ScanEvents() runs on M3UA_CT { var ASP_MTP3_TRANSFERreq vl_ASP_MTP3_TRANSFERreq; alt { [] MTP3_SP_PORT.receive(ASP_MTP3_TRANSFERreq : ?) -> value vl_ASP_MTP3_TRANSFERreq { f_Send_MTP3_TRANSFERreq(vl_ASP_MTP3_TRANSFERreq); repeat; } [] as_SCTP_CommunicationUp(); [] as_SCTP_DataArrive(); [] as_SCTP_Connected(); [] as_Unexpected_SCTP_Events(); [] as_handleM3UA_timers(); [] as_handleSCTP_timers(); } } function f_Send_MTP3_TRANSFERreq(ASP_MTP3_TRANSFERreq pl_ASP_MTP3_TRANSFERreq) runs on M3UA_CT { if (v_Entity.commStatus == aSP_Active) { if (tsp_SCTP_PayloadProtocolID == 3) { // M3UA SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 1, enc_PDU_M3UA (valueof (t_PDU_M3UA_DATA (omit, v_Entity.rctx, t_M3UA_Protocol_Data (int2oct(pl_ASP_MTP3_TRANSFERreq.opc, 4), // OPC int2oct(pl_ASP_MTP3_TRANSFERreq.dpc, 4), // DPC bit2oct('0000'B & pl_ASP_MTP3_TRANSFERreq.sio.si), // SIO bit2oct('000000'B & pl_ASP_MTP3_TRANSFERreq.sio.ni), bit2oct('000000'B & pl_ASP_MTP3_TRANSFERreq.sio.prio), int2oct(pl_ASP_MTP3_TRANSFERreq.sls, 1), // SLS pl_ASP_MTP3_TRANSFERreq.data), omit))), tsp_SCTP_PayloadProtocolID)); } else { // Non-M3UA SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 1, pl_ASP_MTP3_TRANSFERreq.data, tsp_SCTP_PayloadProtocolID)); } if (tsp_logVerbose) { log("MTP3_SP_PORT: ASP_MTP3_TRANSFERreq received -> message sent " & "via SCTP"); } } else { // If the SCTP association is not yet running, we have to buffer the data // messages arrived from the MTP3 side. Sending of buffered data messages // should occure when the SCTP association is up and before sending the // data message in reply for a new ASP_MTP3_TRANSFERreq data message. The // buffer should be checked before sending. v_TRANSFERreq_Buffer[sizeof(v_TRANSFERreq_Buffer)] := pl_ASP_MTP3_TRANSFERreq; if (tsp_logVerbose) { log("MTP3_SP_PORT: ASP_MTP3_TRANSFERreq received in an inactive state " & "-> message was buffered"); } } } // Handle communication up messages of users which performed associate earlier. // We have only one association. altstep as_SCTP_CommunicationUp() runs on M3UA_CT { [] SCTP_PORT.receive(tr_S_SCTP_CommunicationUp(?)) -> value v_ASP_SCTP_ASSOC_CHANGE { if (v_Entity.sCTP_Assoc_ID == v_ASP_SCTP_ASSOC_CHANGE.client_id) { if (v_Entity.commStatus == aSP_Down_sCTP_Associate_done) { v_Entity.commStatus := aSP_Down_commUP_Received; if (tsp_SCTP_PayloadProtocolID != 3) { // Non-M3UA v_Entity.commStatus := aSP_Active; var integer v_i; for (v_i := 0; v_i < sizeof(v_TRANSFERreq_Buffer); v_i := v_i + 1) { log("Sending buffered message #", v_i); f_Send_MTP3_TRANSFERreq(v_TRANSFERreq_Buffer[v_i]); } v_TRANSFERreq_Buffer := {}; // MTP3_SP_PORT.send(ASP_MTP3_RESUME : {}); } if (tsp_logVerbose) { log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } if ((not tsp_M3UA_Server_Mode) and (tsp_SCTP_PayloadProtocolID == 3)) { // M3UA f_ASPUP_Sending(); } } else { if (tsp_logVerbose) { log("SCTP_CommunicationUp received in wrong state (i.e. not after " & "SCTP_Associate is done) in state: ", v_Entity.commStatus); } } } else { if (tsp_logVerbose) { log("Association does not exists, received in CommunicationUp"); } } repeat; } } // This altstep handles the data received from SCTP. altstep as_SCTP_DataArrive() runs on M3UA_CT { [] SCTP_PORT.receive(tr_S_SCTP_DataArrive (?, // associationID ?, // streamID ?, // protocolID ? // data )) -> value v_ASP_SCTP { // Checking the identifier for the association is not necessary, because we // have only only one association. if (f_Assoc_Exists(v_ASP_SCTP.client_id)) { if (tsp_logVerbose) { log("Message received on association #", v_Entity.sCTP_Assoc_ID); } if (tsp_SCTP_PayloadProtocolID == 3) { // M3UA v_PDU_M3UA := dec_PDU_M3UA(v_ASP_SCTP.data); f_handle_M3UA_msg(v_PDU_M3UA); } else { // Non-M3UA f_handle_nonM3UA_msg(v_ASP_SCTP.data); } } else{ log("Message received on unknown association #", v_Entity.sCTP_Assoc_ID, " -> closing connection"); SCTP_PORT.send(t_ASP_SCTP_Close(v_Entity.sCTP_Assoc_ID)); log("SCTP connection closed"); } repeat; } } // Handle the SCTP connected messages. It is sent from the SCTP side and it // signals, that we're on the right track to create the association. This is // for server mode. altstep as_SCTP_Connected() runs on M3UA_CT { [tsp_SCTP_Server_Mode] SCTP_PORT.receive(tr_ASP_SCTP_Connected(?, ?, ?, ?, ?)) -> value v_ASP_SCTP_Connected { // Message from the configured endpoint. if ((v_ASP_SCTP_Connected.local_portnumber == v_Entity.assoc.local_sctp_port) and (v_ASP_SCTP_Connected.local_hostname == v_Entity.assoc.local_ip_addr) and (v_ASP_SCTP_Connected.peer_portnumber == v_Entity.assoc.remote_sctp_port) and (v_ASP_SCTP_Connected.peer_hostname == v_Entity.assoc.remote_ip_addr)) { v_Entity.sCTP_Assoc_ID := v_ASP_SCTP_Connected.client_id; v_Entity.commStatus := aSP_Down_sCTP_Associate_done; log("ASP_SCTP_Connected -> accepted connection from client: ", v_ASP_SCTP_Connected.peer_hostname, ":", v_ASP_SCTP_Connected.peer_portnumber, " on server: ", v_ASP_SCTP_Connected.local_hostname, ":", v_ASP_SCTP_Connected.local_portnumber, " with association #", v_Entity.sCTP_Assoc_ID); } else { log("ASP_SCTP_Connected -> connection from unknown client: ", v_ASP_SCTP_Connected.peer_hostname, ":", v_ASP_SCTP_Connected.peer_portnumber); } repeat; } } // Handle error messages of users. altstep as_Unexpected_SCTP_Events() runs on M3UA_CT { // Handle communications lost message. State of user with given index jumps // back to initial state and stays there. That user will not be able to // communicate anymore. [] SCTP_PORT.receive(tr_S_SCTP_CommunicationLost(?)) -> value v_ASP_SCTP_ASSOC_CHANGE { if (f_Assoc_Exists(v_ASP_SCTP_ASSOC_CHANGE.client_id)) { if (v_Entity.commStatus == aSP_Active) { // MTP3_SP_PORT.send(ASP_MTP3_PAUSE : {}); } v_Entity.commStatus := aSP_Down_sCTP_Initialize_Done; v_Entity.sCTP_Assoc_ID := omit; if (tsp_logVerbose) { log("SCTP_CommunicationLost received"); log("Association #", v_Entity.sCTP_Assoc_ID, " cleared, state " & "changed to: ", v_Entity.commStatus); } } else { if (tsp_logVerbose) { log("Association does not exist, received in CommunicationLost"); } } repeat; } [] SCTP_PORT.receive(tr_S_SCTP_ShutdownComplete(?)) -> value v_ASP_SCTP_ASSOC_CHANGE { if (f_Assoc_Exists(v_ASP_SCTP_ASSOC_CHANGE.client_id)) { if (v_Entity.commStatus == aSP_Active) { // MTP3_SP_PORT.send(ASP_MTP3_PAUSE : {}); } v_Entity.commStatus := aSP_Down_sCTP_Initialize_Done; if (tsp_logVerbose) { log("SCTP_ShutdownComplete received"); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } else { if (tsp_logVerbose) { log("Association does not exist, received in ShutdownComplete"); } } repeat; } [] SCTP_PORT.receive(tr_S_SCTP_ShutdownEvent(?)) -> value v_ASP_SCTP_SHUTDOWN_EVENT { if (f_Assoc_Exists(v_ASP_SCTP_SHUTDOWN_EVENT.client_id)) { if (v_Entity.commStatus == aSP_Active) { // MTP3_SP_PORT.send(ASP_MTP3_PAUSE : {}); } v_Entity.commStatus := aSP_Down_sCTP_Initialize_Done; if (tsp_logVerbose) { log("SCTP_ShutdownEvent received"); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } else { if (tsp_logVerbose) { log("Association does not exist, received in ShutdownEvent"); } } repeat; } [] SCTP_PORT.receive(tr_ASP_SCTP_Connected(?, ?, ?, ?, ?)) -> value v_ASP_SCTP_Connected { log("Unexpected ASP_SCTP_Connected"); repeat; } [] SCTP_PORT.receive(tr_S_SCTP_Restart(?)) -> value v_ASP_SCTP_ASSOC_CHANGE { if (f_Assoc_Exists(v_ASP_SCTP_ASSOC_CHANGE.client_id)) { log("SCTP_Restart received"); v_Entity.commStatus := aSP_Down_commUP_Received; if (tsp_logVerbose) { log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } else { if (tsp_logVerbose) { log("Association does not exist, received in SCTP_Restart"); } } repeat; } [] SCTP_PORT.receive(t_ASP_SCTP_SEND_FAILED(?)) -> value v_ASP_SCTP_SEND_FAILED { log("SCTP_Send failed for association #", v_Entity.sCTP_Assoc_ID); if (f_Assoc_Exists(v_ASP_SCTP_SEND_FAILED.client_id)) { // Daemon sends an error status message here. // MTP3_SP_PORT.send(ASP_MTP3_PAUSE : {}); } else { log("Send error received for association that doesn't exist"); } repeat; } [] SCTP_PORT.receive(tr_S_SCTP_CANT_STR_ASSOC(?)) { repeat; } [] SCTP_PORT.receive { repeat; } } // After reception of SCTP_CommunicationUp M3UA ASPUP/ASPAC is resent by the // entity if it didn't receive ASPUP_Ack/ASPAC_Ack. altstep as_handleM3UA_timers() runs on M3UA_CT { [] T_ASPUP_resend.timeout { if ((v_Entity.commStatus == aSP_Down_commUP_Received) or (v_Entity.commStatus == aSP_Down_ASPUP_Sent)) { // Try to send ASPUP again. SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA(valueof(t_PDU_M3UA_ASPUP(omit, omit))), tsp_SCTP_PayloadProtocolID)); v_Entity.commStatus := aSP_Down_ASPUP_Sent; if (tsp_logVerbose) { log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } T_ASPUP_resend.start; repeat; } [] T_ASPAC_resend.timeout { if ((v_Entity.commStatus == aSP_Inactive) or (v_Entity.commStatus == aSP_Inact_ASPAC_Sent)) { SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA(valueof(t_PDU_M3UA_ASPAC(omit, v_Entity.rctx, omit))), tsp_SCTP_PayloadProtocolID)); v_Entity.commStatus := aSP_Inact_ASPAC_Sent; if (tsp_logVerbose) { log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } T_ASPAC_resend.start; repeat; } [tsp_Enable_M3UA_Heartbeat] T_Heartbeat.timeout { if (v_Entity.commStatus == aSP_Active) { SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA(valueof(t_PDU_M3UA_Heartbeat(omit))), tsp_SCTP_PayloadProtocolID)); if (tsp_logVerbose) { log("Heartbeat sent to association #", v_Entity.sCTP_Assoc_ID); } } T_Heartbeat.start; repeat; } } // Handles SCTP timer events. In server mode we don't associate. altstep as_handleSCTP_timers() runs on M3UA_CT { [not tsp_SCTP_Server_Mode] T_Assoc_restart.timeout { if (v_Entity.commStatus == aSP_Down_sCTP_Initialize_Done) { f_Associate(); } T_Assoc_restart.start; repeat; } } // After reception of SCTP CommunicationUp messages M3UA ASPUP is sent by // every entity and the M3UA ASPUP_Ack is received by every entity. function f_ASPUP_Sending() runs on M3UA_CT { SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, // streamID enc_PDU_M3UA(valueof(t_PDU_M3UA_ASPUP(omit, omit))), tsp_SCTP_PayloadProtocolID)); v_Entity.commStatus := aSP_Down_ASPUP_Sent; if (tsp_logVerbose) { log("M3UA_ASPUP sent"); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } // Test if an association with assocID exists or not. We have only one // association at the moment, we just check if the given assocID is the same, // that is associated with our single entity. If we would have more entities // in a table, the index of it should be returned instead of a boolean value. function f_Assoc_Exists(integer pl_assocID) runs on M3UA_CT return boolean { if (ispresent(v_Entity.sCTP_Assoc_ID) and v_Entity.sCTP_Assoc_ID == pl_assocID) { return true; } else { if (tsp_logVerbose) { log("Association #", v_Entity.sCTP_Assoc_ID, " not found"); } } return false; } function f_handle_M3UA_msg(PDU_M3UA pl_PDU_M3UA) runs on M3UA_CT { if (ischosen(pl_PDU_M3UA.m3UA_DATA)) { if (v_Entity.commStatus == aSP_Active) { // Send ASP_MTP3_TRANSFERind message. MTP3_SP_PORT.send (valueof (tr_ASP_MTP3_TRANSFERind_sio (substr(oct2bit(pl_PDU_M3UA.m3UA_DATA.messageParameters.protocol_Data.nI), 6, 2), substr(oct2bit(pl_PDU_M3UA.m3UA_DATA.messageParameters.protocol_Data.mP), 6, 2), substr(oct2bit(pl_PDU_M3UA.m3UA_DATA.messageParameters.protocol_Data.sI), 4, 4), oct2int(pl_PDU_M3UA.m3UA_DATA.messageParameters.protocol_Data.oPC), oct2int(pl_PDU_M3UA.m3UA_DATA.messageParameters.protocol_Data.dPC), oct2int(pl_PDU_M3UA.m3UA_DATA.messageParameters.protocol_Data.sLS), pl_PDU_M3UA.m3UA_DATA.messageParameters.protocol_Data.userProtocolData))); if (tsp_logVerbose) { log("MTP3_SP_PORT: Data received -> TRANSFERind sent"); } } else { // Buffering indication messages? if (tsp_logVerbose) { log("MTP3_SP_PORT: Data received, no user connected -> discard"); } } } else if (ischosen(pl_PDU_M3UA.m3UA_BEAT)) { if (v_Entity.commStatus == aSP_Active) { SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA (valueof (t_PDU_M3UA_Beat_Ack (pl_PDU_M3UA.m3UA_BEAT.messageParameters.heartbeat_Data))), tsp_SCTP_PayloadProtocolID)); if (tsp_logVerbose) { log("M3UA_BEAT received -> M3UA_BEAT_Ack sent"); } } else { if (tsp_logVerbose) { log("M3UA_BEAT received in wrong state"); } } } else if (ischosen(pl_PDU_M3UA.m3UA_BEAT_Ack)) { if (tsp_logVerbose) { log("Received M3UA_BEAT_Ack -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_ERR)) { if (tsp_logVerbose) { log("Received M3UA_ERR -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_NOTIFY)) { if (tsp_logVerbose) { log("Received M3UA_NOTIFY -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_DUNA)) { if (tsp_logVerbose) { log("Received M3UA_DUNA -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_DAVA)) { if (tsp_logVerbose) { log("Received M3UA_DAVA -> discard"); } } // In server mode ASP_M3UA_DAUD messages can be received. In response the // server must send ASP_M3UA_DAVA messages. It's not checked if we're // servers or not. else if (ischosen(pl_PDU_M3UA.m3UA_DAUD)) { if ((v_Entity.commStatus == aSP_Inactive) or (v_Entity.commStatus == aSP_Inact_ASPAC_Sent) or (v_Entity.commStatus == aSP_Active)) { // Send ASP_M3UA_DAVA message. SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA (valueof (t_PDU_M3UA_DAVA (pl_PDU_M3UA.m3UA_DAUD.messageParameters.network_Appearance, pl_PDU_M3UA.m3UA_DAUD.messageParameters.routing_Context, pl_PDU_M3UA.m3UA_DAUD.messageParameters.affected_Point_Codes, pl_PDU_M3UA.m3UA_DAUD.messageParameters.info_String))), tsp_SCTP_PayloadProtocolID)); if (tsp_logVerbose) { log("M3UA_DAUD received -> DAVA sent"); } } else { if (tsp_logVerbose) { log("M3UA_DAUD received in wrong state"); } } } else if (ischosen(pl_PDU_M3UA.m3UA_SCON)) { if (tsp_logVerbose) { log("Received M3UA_SCON -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_DUPU)) { if (tsp_logVerbose) { log("Received M3UA_DUPU -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_DRST)) { if (tsp_logVerbose) { log("Received M3UA_DRST -> discard"); } } // In server mode we can receive M3UA_ASPUP messages. The answer will be a // M3UA_ASPUP_Ack message to the client, followed by a NTFY if AS state changed. else if (ischosen(pl_PDU_M3UA.m3UA_ASPUP)) { if (((v_Entity.commStatus == aSP_Down_commUP_Received) or (v_Entity.commStatus == aSP_Down_ASPUP_Sent)) and tsp_M3UA_Server_Mode) { v_Entity.commStatus := aSP_Inactive; SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA(valueof(t_PDU_M3UA_ASPUP_Ack)), tsp_SCTP_PayloadProtocolID)); if (tsp_logVerbose) { log("M3UA_ASPUP received -> M3UA_ASPUP_Ack sent"); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } /* AS state change -> send NTFY (see RFC4666 4.3.4.5) */ SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA (valueof (t_PDU_M3UA_NOTIFY /* type AS-State_Change (1), Status Information AS-INACTIVE (2) */ (t_Status('0001'O, '0002'O), omit, v_Entity.rctx, omit))), tsp_SCTP_PayloadProtocolID)); if (tsp_logVerbose) { log("AS state changed -> M3UA_NOTIFY sent"); } } else { if (tsp_logVerbose) { log("M3UA_ASPUP received in wrong state or the emulation is not in " & "M3UA server mode"); } } } // Receives a M3UA_ASPDN message and sends a M3UA_ASPDN_Ack message in // response. else if (ischosen(pl_PDU_M3UA.m3UA_ASPDN)) { if ((v_Entity.commStatus == aSP_Inactive) or (v_Entity.commStatus == aSP_Inact_ASPAC_Sent) or (v_Entity.commStatus == aSP_Active)) { SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA(valueof(t_PDU_M3UA_ASPDN_Ack)), tsp_SCTP_PayloadProtocolID)); if (v_Entity.commStatus == aSP_Active) { // MTP3_SP_PORT.send(ASP_MTP3_PAUSE : {}); } v_Entity.commStatus := aSP_Down_commUP_Received; if (tsp_logVerbose) { log("M3UA_ASPDN received -> ASPDN_Ack sent"); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } else { if (tsp_logVerbose) { log("ASPDN received in wrong state or the emulation is not in M3UA " & "server mode"); } } } // The M3UA client receives M3UA_ASPUP_Ack messages from the server. In // response of a M3UA_ASPUP message sent by the client. else if (ischosen(pl_PDU_M3UA.m3UA_ASPUP_Ack)) { if (((v_Entity.commStatus == aSP_Down_ASPUP_Sent) or (v_Entity.commStatus == aSP_Inactive)) and not tsp_M3UA_Server_Mode) { v_Entity.commStatus := aSP_Inactive; if (tsp_logVerbose) { log("M3UA_ASPUP_Ack received -> send M3UA_ASPAC"); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA(valueof(t_PDU_M3UA_ASPAC(omit, v_Entity.rctx, omit))), tsp_SCTP_PayloadProtocolID)); // The state changes again after sending the M3UA_ASPAC message. v_Entity.commStatus := aSP_Inact_ASPAC_Sent; if (tsp_logVerbose) { log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } else { if (tsp_logVerbose) { log("M3UA_ASPUP_Ack received in wrong state or the emulation is not " & "in M3UA client mode"); } } } else if (ischosen(pl_PDU_M3UA.m3UA_ASPDN_Ack)) { if (tsp_logVerbose) { log("Received M3UA_ASPDN_Ack -> discard"); } } // M3UA_ASPAC messages are received on the server side. The server sends a // M3UA_ASPAC_Ack message back to the client. This step makes the // association active on both sides. else if (ischosen(pl_PDU_M3UA.m3UA_ASPAC)) { if (((v_Entity.commStatus == aSP_Inactive) or (v_Entity.commStatus == aSP_Inact_ASPAC_Sent)) and tsp_M3UA_Server_Mode) { v_Entity.commStatus := aSP_Active; if (tsp_logVerbose) { log("M3UA_ASPAC received -> M3UA_ASPAC_Ack sent"); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } var integer v_i; for (v_i := 0; v_i < sizeof(v_TRANSFERreq_Buffer); v_i := v_i + 1) { log("Sending buffered message #", v_i); f_Send_MTP3_TRANSFERreq(v_TRANSFERreq_Buffer[v_i]); } v_TRANSFERreq_Buffer := {}; // Send M3UA_ASPAC_Ack. SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA (valueof (t_PDU_M3UA_ASPAC_Ack (pl_PDU_M3UA.m3UA_ASPAC.messageParameters.traffic_Mode_Type, pl_PDU_M3UA.m3UA_ASPAC.messageParameters.routing_Context))), tsp_SCTP_PayloadProtocolID)); // MTP3_SP_PORT.send(ASP_MTP3_RESUME : {}); /* AS state change -> send NTFY (see RFC4666 4.3.4.5) */ SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA (valueof (t_PDU_M3UA_NOTIFY /* tag 0xd, len 8, type AS-State_Change (1), Status Information AS-ACTIVE (1) */ (t_Status('0001'O, '0001'O), omit, v_Entity.rctx, omit))), tsp_SCTP_PayloadProtocolID)); if (tsp_logVerbose) { log("AS state changed -> M3UA_NOTIFY sent"); } } else { if (tsp_logVerbose) { log("M3UA_ASPAC received in wrong state or the emulation is not in " & "M3UA server mode"); } } } // The client receives M3UA_ASPAC_Ack messages from the server. The // association will be activated. The buffered messages should be send here. else if (ischosen(pl_PDU_M3UA.m3UA_ASPAC_Ack)) { if (((v_Entity.commStatus == aSP_Inact_ASPAC_Sent) or (v_Entity.commStatus == aSP_Active)) and not tsp_M3UA_Server_Mode) { // MTP3_SP_PORT.send(ASP_MTP3_RESUME : {}); v_Entity.commStatus := aSP_Active; if (tsp_logVerbose) { log("ASPAC_Ack received for association #", v_Entity.sCTP_Assoc_ID); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } var integer v_i; for (v_i := 0; v_i < sizeof(v_TRANSFERreq_Buffer); v_i := v_i + 1) { log("Sending buffered message #", v_i); f_Send_MTP3_TRANSFERreq(v_TRANSFERreq_Buffer[v_i]); } v_TRANSFERreq_Buffer := {}; } else { if (tsp_logVerbose) { log("M3UA_ASPAC_Ack received in wrong state on association #", v_Entity.sCTP_Assoc_ID, " or the emulation is not in M3UA " & "client mode"); } } } // Receives a M3UA_ASPIA message and sends back a M3UA_ASPIA_Ack message in // response. else if (ischosen(pl_PDU_M3UA.m3UA_ASPIA)) { if (v_Entity.commStatus == aSP_Active) { SCTP_PORT.send (t_S_SCTP_Send (v_Entity.sCTP_Assoc_ID, 0, enc_PDU_M3UA (valueof (t_PDU_M3UA_ASPIA_Ack (pl_PDU_M3UA.m3UA_ASPIA.messageParameters.routing_Context))), tsp_SCTP_PayloadProtocolID)); // MTP3_SP_PORT.send(ASP_MTP3_PAUSE : {}); v_Entity.commStatus := aSP_Inactive; if (tsp_logVerbose) { log("M3UA_ASPIA received -> M3UA_ASPIA_Ack sent"); log("Association #", v_Entity.sCTP_Assoc_ID, " state changed to: ", v_Entity.commStatus); } } else { if (tsp_logVerbose) { log("M3UA_ASPIA received in wrong state or the emulation is not " & "running in M3UA server mode"); } } } else if (ischosen(pl_PDU_M3UA.m3UA_ASPIA_Ack)) { if (tsp_logVerbose) { log("Received M3UA_ASPIA_Ack -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_REG_REQ)) { if (tsp_logVerbose) { log("Received M3UA_REG_REQ -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_REG_RSP)) { if (tsp_logVerbose) { log("Received M3UA_REG_RSP -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_DEREG_REQ)) { if (tsp_logVerbose) { log("Received M3UA_DEREG_REQ -> discard"); } } else if (ischosen(pl_PDU_M3UA.m3UA_DEREG_RSP)) { if (tsp_logVerbose) { log("Received M3UA_DEREG_RSP -> discard"); } } } function f_handle_nonM3UA_msg(octetstring pl_PDU) runs on M3UA_CT { if (v_Entity.commStatus == aSP_Active) { // Send TRANSFERind message. MTP3_SP_PORT.send(valueof (tr_ASP_MTP3_TRANSFERind_sio ('00'B, '00'B, '0000'B, 0, 0, 0, pl_PDU))); if (tsp_logVerbose) { log("Non-M3UA DATA received -> TRANSFERind sent"); } } else { if (tsp_logVerbose) { log("DATA received, but no user connected -> discard"); } } } }